diff --git a/apps/server/package.json b/apps/server/package.json index 57ce643c1..7c887a9be 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -62,6 +62,7 @@ "better-auth": "catalog:", "cjk-regex": "^3.5.0", "css-tree": "^3.2.1", + "docx": "^9.8.1", "drizzle-orm": "catalog:", "drizzle-zod": "1.0.0-beta.14-a36c63d", "es-toolkit": "catalog:", @@ -75,6 +76,7 @@ "node-html-parser": "^9.0.4", "nodemailer": "^10.0.13", "ollama-ai-provider-v2": "^4.0.1", + "pdfjs-dist": "6.3.289", "pg": "catalog:", "react": "catalog:", "react-email": "^6.11.0", diff --git a/apps/server/src/http/app.test.ts b/apps/server/src/http/app.test.ts index 8ab47b79b..41131d9cc 100644 --- a/apps/server/src/http/app.test.ts +++ b/apps/server/src/http/app.test.ts @@ -239,3 +239,16 @@ it.each(["/auth/consent", "/auth/consent/", "/auth/login"])("prevents framing or expect(response.headers.get("referrer-policy")).toBe("no-referrer"); expect(response.headers.get("cache-control")).toBe("no-store"); }); + +it("rejects oversized REST requests before parsing multipart uploads", async () => { + const { createApp } = await import("./app"); + const response = await createApp().request("http://localhost:3000/api/openapi/files", { + method: "POST", + headers: { "Content-Length": String(40 * 1024 * 1024 + 1) }, + body: "x", + }); + expect(response.status).toBe(413); + expect(await response.json()).toMatchObject({ code: "PAYLOAD_TOO_LARGE", status: 413 }); + expect(response.headers.get("cache-control")).toBe("no-store"); + expect(mocks.handleOpenApi).not.toHaveBeenCalled(); +}); diff --git a/apps/server/src/http/app.ts b/apps/server/src/http/app.ts index 25fcba142..fc0797e06 100644 --- a/apps/server/src/http/app.ts +++ b/apps/server/src/http/app.ts @@ -3,6 +3,7 @@ import type { Context } from "hono"; import { BlockList, isIP } from "node:net"; import { getConnInfo } from "@hono/node-server/conninfo"; import { Hono } from "hono"; +import { bodyLimit } from "hono/body-limit"; import { compress } from "hono/compress"; import { prepareStagedBody, withStagedBody } from "@reactive-resume/api/features/storage/transport"; import { env } from "@reactive-resume/env/server"; @@ -73,6 +74,17 @@ export function createApp(options: AppOptions = {}) { c.header("Cache-Control", "no-store"); }); + app.use( + "/api/openapi/*", + bodyLimit({ + maxSize: 40 * 1024 * 1024, + onError: (c) => { + c.header("Cache-Control", "no-store"); + return c.json({ defined: false, code: "PAYLOAD_TOO_LARGE", status: 413, message: "Payload too large" }, 413); + }, + }), + ); + app.post("/api/storage/stage", (c) => prepareStagedBody(c.req.raw)); app.all("/api/rpc", (c) => withStagedBody(c.req.raw, (request) => handleRpc(request, client(c)))); app.all("/api/rpc/*", (c) => withStagedBody(c.req.raw, (request) => handleRpc(request, client(c)))); diff --git a/apps/server/src/openapi/generator.test.ts b/apps/server/src/openapi/generator.test.ts index 49539fcc1..4115f6b29 100644 --- a/apps/server/src/openapi/generator.test.ts +++ b/apps/server/src/openapi/generator.test.ts @@ -91,3 +91,55 @@ describe("generateOpenApiSpec", () => { expect(findImpossibleRequestSchemas(spec)).toEqual([]); }); }); + +it("documents anonymous routes, all supported credentials, and additive PATCH routes", async () => { + const spec = await generateSpec(); + expect(spec.paths?.["/flags"]?.get?.security).toEqual([]); + expect(spec.paths?.["/resumes/{username}/{slug}"]?.get?.security).toEqual([]); + expect(spec.paths?.["/resumes"]?.get?.security).toEqual([{ apiKey: [] }, { bearerAuth: [] }, { cookieAuth: [] }]); + for (const path of ["/applications/{id}", "/cover-letters/{id}"]) { + expect(spec.paths?.[path]?.put?.requestBody).toBeDefined(); + expect(spec.paths?.[path]?.patch?.requestBody).toBeDefined(); + } + expect(spec.paths?.["/files"]?.post?.requestBody).toHaveProperty("content.multipart/form-data"); + expect(spec.paths?.["/files"]?.post?.requestBody).not.toHaveProperty("content.application/json"); + expect(spec.paths?.["/resumes"]?.get?.parameters).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: "limit", in: "query" }), + expect.objectContaining({ name: "offset", in: "query" }), + ]), + ); +}); + +it("gives every published app operation an explicit route and input/output contracts", async () => { + const { openAPIRouter } = await import("./generator"); + const visit = (node: unknown, path: string) => { + if (!node || typeof node !== "object") return; + if ("~orpc" in node) { + const definition = node["~orpc"] as { + route: { tags?: string[]; method?: string; path?: string }; + inputSchema?: unknown; + outputSchema?: unknown; + }; + if (definition.route.tags?.includes("Internal")) return; + expect(definition.route.method, path).toBeDefined(); + expect(definition.route.path, path).toBeDefined(); + expect(definition.inputSchema, path).toBeDefined(); + expect(definition.outputSchema, path).toBeDefined(); + return; + } + for (const [key, value] of Object.entries(node)) visit(value, `${path}.${key}`); + }; + visit(openAPIRouter, "api"); +}); + +it("describes empty responses and common errors without impossible payloads", async () => { + const spec = await generateSpec(); + const deleted = spec.paths?.["/files"]?.delete?.responses?.["200"]; + expect(deleted).toBeDefined(); + expect(deleted).not.toHaveProperty("content"); + expect(spec.paths?.["/resumes"]?.get?.responses?.default).toHaveProperty( + "content.application/json.schema.properties.code", + ); + expect(spec.paths?.["/resumes"]?.get?.responses?.["200"]).toHaveProperty("headers.X-Total-Count"); +}); diff --git a/apps/server/src/openapi/generator.ts b/apps/server/src/openapi/generator.ts index 532b52008..3d1f6ec90 100644 --- a/apps/server/src/openapi/generator.ts +++ b/apps/server/src/openapi/generator.ts @@ -2,6 +2,7 @@ import type { OpenAPI } from "@orpc/openapi"; import { OpenAPIGenerator } from "@orpc/openapi"; import { JSON_SCHEMA_INPUT_REGISTRY, ZodToJsonSchemaConverter } from "@orpc/zod/zod4"; import { downloadResumePdfProcedure } from "@reactive-resume/api/features/resume/export"; +import { restAliases } from "@reactive-resume/api/rest"; import router from "@reactive-resume/api/routers"; import { resumeDataSchema } from "@reactive-resume/schema/resume/data"; import { createResumeDataJsonSchema } from "@reactive-resume/schema/resume/json-schema"; @@ -9,6 +10,7 @@ import { writableResumeDataSchema } from "@reactive-resume/schema/resume/write"; export const openAPIRouter = { ...router, + rest: restAliases, resume: { ...router.resume, downloadPdf: downloadResumePdfProcedure, @@ -79,11 +81,12 @@ const healthResponseSchema = { } satisfies OpenAPI.SchemaObject; export async function generateOpenApiSpec({ appUrl, version }: GenerateOpenApiSpecOptions) { - return await openAPIGenerator.generate(openAPIRouter, { + const spec = await openAPIGenerator.generate(openAPIRouter, { info: { title: "Reactive Resume", version, - description: "Reactive Resume API", + description: + "Reactive Resume API. Mutations do not support Idempotency-Key. Do not automatically retry POST, PUT, PATCH or DELETE requests after a timeout: first read the resource to determine whether the operation succeeded. Existing enum values and response shapes are retained for compatibility.", license: { name: "MIT", url: "https://github.com/reactive-resume/reactive-resume/blob/main/LICENSE" }, contact: { name: "Amruth Pillai", email: "hello@amruthpillai.com", url: "https://amruthpillai.com" }, }, @@ -116,6 +119,19 @@ export async function generateOpenApiSpec({ appUrl, version }: GenerateOpenApiSp }, components: { securitySchemes: { + bearerAuth: { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + description: "An OAuth access token issued by this instance for its API/MCP resource.", + }, + cookieAuth: { + type: "apiKey", + in: "cookie", + name: "better-auth.session_token", + description: + "Browser session (secure deployments use the __Secure- prefix). Cookie requests must originate from this instance.", + }, apiKey: { type: "apiKey", name: "x-api-key", @@ -124,7 +140,66 @@ export async function generateOpenApiSpec({ appUrl, version }: GenerateOpenApiSp }, }, }, - security: [{ apiKey: [] }], + security: [{ apiKey: [] }, { bearerAuth: [] }, { cookieAuth: [] }], filter: ({ contract }) => !contract["~orpc"].route.tags?.includes("Internal"), }); + // Void results have no HTTP body; Zod's impossible JSON schema is not a response payload. + const isVoid = (schema: unknown): boolean => { + if (!schema || typeof schema !== "object") return false; + const value = schema as { not?: object; anyOf?: unknown[] }; + return ( + (value.not !== undefined && Object.keys(value.not).length === 0) || + (Array.isArray(value.anyOf) && value.anyOf.every(isVoid)) + ); + }; + for (const [path, item] of Object.entries(spec.paths ?? {})) { + if (!item || path === "/api/health") continue; + for (const method of ["get", "post", "put", "patch", "delete"] as const) { + const operation = item[method]; + if (!operation) continue; + const body = operation.requestBody; + if (body && !("$ref" in body) && body.content["multipart/form-data"]) delete body.content["application/json"]; + operation.responses ??= {}; + operation.responses.default = { + description: "Structured API error. See status and code; do not branch on message text.", + content: { + "application/json": { + schema: { + type: "object", + required: ["defined", "code", "status", "message"], + properties: { + defined: { type: "boolean" }, + code: { type: "string" }, + status: { type: "integer" }, + message: { type: "string" }, + data: {}, + }, + }, + }, + }, + }; + for (const response of Object.values(operation.responses)) { + if ("$ref" in response) continue; + const schema = response.content?.["application/json"]?.schema; + if (isVoid(schema)) delete response.content; + if ( + schema && + "type" in schema && + schema.type === "array" && + operation.parameters?.some((parameter) => "name" in parameter && parameter.name === "limit") + ) { + response.headers = { + ...response.headers, + "X-Total-Count": { description: "Total matching results before pagination.", schema: { type: "integer" } }, + "X-Limit": { description: "Page size, when pagination is requested.", schema: { type: "integer" } }, + "X-Offset": { + description: "Zero-based offset, when pagination is requested.", + schema: { type: "integer" }, + }, + }; + } + } + } + } + return spec; } diff --git a/apps/server/src/openapi/handler.test.ts b/apps/server/src/openapi/handler.test.ts new file mode 100644 index 000000000..0b5cb91b9 --- /dev/null +++ b/apps/server/src/openapi/handler.test.ts @@ -0,0 +1,70 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("@reactive-resume/auth/config", () => ({ + auth: { + api: { getSession: vi.fn().mockResolvedValue(null), verifyApiKey: vi.fn().mockResolvedValue({ valid: false }) }, + }, + verifyOAuthToken: vi.fn().mockRejectedValue(new Error("invalid token")), + isCustomOAuthProviderEnabled: () => false, +})); +const { handleOpenApi } = await import("./handler"); +const request = (path: string, init?: RequestInit) => + handleOpenApi(new Request(`http://localhost:3000/api/openapi${path}`, init), "127.0.0.1"); + +beforeEach(() => { + vi.spyOn(console, "error").mockImplementation(() => {}); +}); + +describe("REST boundary", () => { + it("serves public configuration without authentication and marks it uncacheable", async () => { + const response = await request("/flags"); + expect(response.status).toBe(200); + expect(await response.json()).toHaveProperty("disableSignups"); + expect(response.headers.get("Cache-Control")).toBe("no-store"); + }); + it.each(["/resume/getRoot", "/storage/uploadFile", "/storage/deleteFile", "/missing"])( + "does not expose internal or unknown routes: %s", + async (path) => { + const response = await request(path, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "{}", + }); + expect(response.status).toBe(404); + expect(await response.json()).toMatchObject({ code: "NOT_FOUND", status: 404 }); + }, + ); + it.each([ + "/resumes", + "/applications", + "/documents", + "/ai-providers", + "/agent/threads", + "/cover-letters", + "/resumes/private/exports/json", + "/cover-letters/private/exports/pdf", + ])("rejects unauthenticated private reads: %s", async (path) => { + const response = await request(path); + expect(response.status).toBe(401); + expect(await response.json()).toMatchObject({ code: "UNAUTHORIZED", status: 401 }); + }); + it("decodes multipart public checks and returns structured errors for invalid PDF content", async () => { + const form = new FormData(); + form.set("file", new File(["not a PDF"], "resume.pdf", { type: "application/pdf" })); + const response = await request("/pdf-checks", { method: "POST", body: form }); + expect(response.status).toBe(400); + expect(await response.json()).toMatchObject({ + code: "BAD_REQUEST", + message: "Provide a PDF no larger than 25 MB.", + }); + }); + it("returns the same JSON error envelope for malformed JSON", async () => { + const response = await request("/resumes", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "{", + }); + expect(response.status).toBe(400); + expect(await response.json()).toMatchObject({ code: "BAD_REQUEST", status: 400 }); + }); +}); diff --git a/apps/server/src/openapi/handler.ts b/apps/server/src/openapi/handler.ts index 8f3b59246..75ad8eb81 100644 --- a/apps/server/src/openapi/handler.ts +++ b/apps/server/src/openapi/handler.ts @@ -1,7 +1,7 @@ import { SmartCoercionPlugin } from "@orpc/json-schema"; import { OpenAPIHandler } from "@orpc/openapi/fetch"; import { onError } from "@orpc/server"; -import { BatchHandlerPlugin, RequestHeadersPlugin, StrictGetMethodPlugin } from "@orpc/server/plugins"; +import { RequestHeadersPlugin, StrictGetMethodPlugin } from "@orpc/server/plugins"; import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4"; import { env } from "@reactive-resume/env/server"; import { appVersion } from "../app-version"; @@ -10,8 +10,8 @@ import { getRequestLocale } from "../rpc/locale"; import { generateOpenApiSpec, openAPIRouter } from "./generator"; const openAPIHandler = new OpenAPIHandler(openAPIRouter, { + filter: ({ contract }) => !contract["~orpc"].route.tags?.includes("Internal"), plugins: [ - new BatchHandlerPlugin(), new RequestHeadersPlugin(), new StrictGetMethodPlugin(), new SmartCoercionPlugin({ @@ -26,7 +26,8 @@ const openAPIHandler = new OpenAPIHandler(openAPIRouter, { }); export async function handleOpenApi(request: Request, trustedClient: string) { - if (request.method === "GET" && (request.url.endsWith("/spec.json") || request.url.endsWith("/spec"))) { + const pathname = new URL(request.url).pathname; + if (request.method === "GET" && ["/api/openapi/spec.json", "/api/openapi/spec"].includes(pathname)) { return Response.json(await generateOpenApiSpec({ appUrl: env.APP_URL, version: appVersion })); } @@ -36,6 +37,12 @@ export async function handleOpenApi(request: Request, trustedClient: string) { context: { locale: getRequestLocale(request), reqHeaders: request.headers, resHeaders, trustedClient }, }); - if (!response) return new Response("NOT_FOUND", { status: 404 }); + resHeaders.set("Cache-Control", "no-store"); + resHeaders.set("X-Content-Type-Options", "nosniff"); + if (!response) + return Response.json( + { defined: false, code: "NOT_FOUND", status: 404, message: "Not found" }, + { status: 404, headers: resHeaders }, + ); return mergeResponseHeaders(response, resHeaders); } diff --git a/apps/server/src/static/uploads-s3.test.ts b/apps/server/src/static/uploads-s3.test.ts index 5d5322fd6..807a206ac 100644 --- a/apps/server/src/static/uploads-s3.test.ts +++ b/apps/server/src/static/uploads-s3.test.ts @@ -16,6 +16,9 @@ vi.mock("@reactive-resume/env/server", () => ({ env: envMock })); type StoredObject = { data: Buffer; contentType: string }; type StorageRequest = { method: string; path: string; headers: IncomingHttpHeaders }; +const resolveUser = vi.hoisted(() => vi.fn()); +vi.mock("@reactive-resume/api/context", () => ({ resolveUserFromRequestHeaders: resolveUser })); + const objects = new Map(); const requests: StorageRequest[] = []; @@ -62,6 +65,7 @@ beforeAll(async () => { }); beforeEach(() => { + resolveUser.mockReset(); objects.clear(); requests.length = 0; }); @@ -102,8 +106,31 @@ it("stores private attachments without ACLs while keeping them outside the publi it.each(["text/html", "image/svg+xml"])("downloads stored %s uploads instead of rendering them", async (type) => { const key = "uploads/user-1/pictures/upload.bin"; await storage.write({ key, data: new TextEncoder().encode(""), contentType: type }); + resolveUser.mockResolvedValue({ id: "user-1" }); const response = await handleUpload(new Request(`${envMock.APP_URL}/api/${key}`)); expect(response.status).toBe(200); expect(response.headers.get("Content-Type")).toBe("application/octet-stream"); expect(response.headers.get("Content-Disposition")).toBe('attachment; filename="upload.bin"'); }); + +it("requires the upload owner before serving application PDFs, including conditional requests", async () => { + const key = "uploads/user-1/pictures/application.pdf"; + await storage.write({ + key, + data: new TextEncoder().encode("private application PDF"), + contentType: "application/pdf", + }); + for (const viewer of [null, { id: "other-user" }]) { + resolveUser.mockResolvedValue(viewer); + const response = await handleUpload( + new Request(`${envMock.APP_URL}/api/${key}`, { headers: { "If-None-Match": '"test-etag"' } }), + ); + expect(response.status).toBe(404); + expect(await response.text()).not.toContain("private application PDF"); + } + resolveUser.mockResolvedValue({ id: "user-1" }); + const response = await handleUpload(new Request(`${envMock.APP_URL}/api/${key}`)); + expect(response.status).toBe(200); + expect(await response.text()).toBe("private application PDF"); + expect(response.headers.get("Cache-Control")).toBe("private, no-store"); +}); diff --git a/apps/server/src/static/uploads.ts b/apps/server/src/static/uploads.ts index 3748c99ae..73e448a47 100644 --- a/apps/server/src/static/uploads.ts +++ b/apps/server/src/static/uploads.ts @@ -21,9 +21,16 @@ export async function handleUpload(request: Request) { const filename = filePath.split("/").pop() ?? filePath; const contentType = storedFile.contentType ?? inferContentType(filename); + const isPublicPicture = filePath.startsWith("pictures/") && INLINE_CONTENT_TYPES.has(contentType); + if (!isPublicPicture) { + const { resolveUserFromRequestHeaders } = await import("@reactive-resume/api/context"); + const user = await resolveUserFromRequestHeaders(request.headers).catch(() => null); + if (user?.id !== userId) + return new Response("Not Found", { status: 404, headers: { "Cache-Control": "no-store" } }); + } const etag = createEtag(storedFile); - if (isNotModified(request.headers, etag)) return makeNotModifiedResponse(etag); + if (isPublicPicture && isNotModified(request.headers, etag)) return makeNotModifiedResponse(etag); const shouldForceDownload = !INLINE_CONTENT_TYPES.has(contentType); @@ -35,7 +42,7 @@ export async function handleUpload(request: Request) { headers.set("Content-Disposition", `attachment; filename="${encodeURIComponent(basename(filename))}"`); } - headers.set("Cache-Control", "public, max-age=31536000, immutable"); + headers.set("Cache-Control", isPublicPicture ? "public, max-age=31536000, immutable" : "private, no-store"); headers.set("ETag", etag); headers.set("X-Content-Type-Options", "nosniff"); headers.set("X-Robots-Tag", "noindex, nofollow"); diff --git a/apps/server/tsdown.config.ts b/apps/server/tsdown.config.ts index 773035f4e..bd0141970 100644 --- a/apps/server/tsdown.config.ts +++ b/apps/server/tsdown.config.ts @@ -41,6 +41,7 @@ const bundledInteropPackages = new Set([ "source-map-js", "launder", "dayjs", + "wink-porter2-stemmer", ]); const packageNameOf = (id: string) => diff --git a/apps/web/src/features/resume/import/pdf-text.ts b/apps/web/src/features/resume/import/pdf-text.ts index b7de4b618..ab358cc0e 100644 --- a/apps/web/src/features/resume/import/pdf-text.ts +++ b/apps/web/src/features/resume/import/pdf-text.ts @@ -1,81 +1,8 @@ -import type { ExtractedDocument } from "@reactive-resume/resume/ats-pdf"; +import { documentToLines } from "@reactive-resume/import/pdf-lines"; import { buildExtractedDocument } from "@reactive-resume/resume/ats-pdf"; import { extractPdf } from "@/features/ats-checker/extract-client"; -/** - * Turns a PDF into the plain lines `parseResumeText` reads. - * - * The extraction itself is the ATS checker's — same worker configuration, same size and password - * guards, same font-relative line clustering and column-gutter detection. Only the joining differs: - * the ATS text is prose, while the importer splits header rows on runs of two or more spaces, so a - * tabbed "Company Role Location" has to survive as three fields rather than one phrase. - */ - -/** Horizontal gap, relative to font size, above which two spans are separate header fields. */ -const FIELD_GAP_RATIO = 1.5; -/** Narrower gaps are still a word break; matches the ratio the ATS extractor joins prose with. */ -const SPACE_GAP_RATIO = 0.25; - -/** The evidence the ATS layout rules require before calling a page two-column. */ -const GUTTER_COVERAGE = 0.85; -const GUTTER_SPLIT_RATIO = 0.25; - -type PageGeometry = ExtractedDocument["pages"][number]; -type TextSpan = PageGeometry["lines"][number]["spans"][number]; - -function joinSpans(spans: readonly TextSpan[]): string { - let text = ""; - let previous: TextSpan | null = null; - - for (const span of spans) { - if (previous) { - const gap = span.x - (previous.x + previous.width); - const fontSize = Math.max(previous.fontSize, span.fontSize, 1); - - if (gap > FIELD_GAP_RATIO * fontSize) text += " "; - else if (gap > SPACE_GAP_RATIO * fontSize && !/\s$/.test(text) && !/^\s/.test(span.text)) text += " "; - } - - text += span.text; - previous = span; - } - - return text.trim(); -} - -/** Mid-point of a convincing gutter, or null on a page that reads as one column. */ -function columnSplitX(page: PageGeometry): number | null { - const gutter = page.gutter; - if (!gutter || gutter.coverage < GUTTER_COVERAGE || gutter.splitRatio < GUTTER_SPLIT_RATIO) return null; - - return gutter.x + gutter.width / 2; -} - -/** - * Lines grouped by baseline alone interleave a sidebar with the body, which drops a whole work - * history into the skills section. On a two-column page each column is read out in full instead. - */ -function pageToLines(page: PageGeometry): string[] { - const splitX = columnSplitX(page); - if (splitX === null) return page.lines.map((line) => joinSpans(line.spans)); - - const left: string[] = []; - const right: string[] = []; - - for (const line of page.lines) { - const leftSpans = line.spans.filter((span) => span.x + span.width / 2 < splitX); - if (leftSpans.length > 0) left.push(joinSpans(leftSpans)); - - const rightSpans = line.spans.filter((span) => span.x + span.width / 2 >= splitX); - if (rightSpans.length > 0) right.push(joinSpans(rightSpans)); - } - - return [...left, ...right]; -} - -export function documentToLines(doc: ExtractedDocument): string[] { - return doc.pages.flatMap(pageToLines).filter((line) => line.length > 0); -} +export { documentToLines } from "@reactive-resume/import/pdf-lines"; export async function extractPdfLines(file: File): Promise { return documentToLines(buildExtractedDocument(await extractPdf(file, { operatorBudgetMs: 0 }))); diff --git a/docs/guides/using-the-api.mdx b/docs/guides/using-the-api.mdx index d35d2e4ef..1b2710d1e 100644 --- a/docs/guides/using-the-api.mdx +++ b/docs/guides/using-the-api.mdx @@ -112,8 +112,52 @@ Every endpoint, with its parameters, request body and responses, is listed in th | AI | `/ai-providers`, `/ai`, `/agent` | Manage your AI providers, run AI tools such as resume import from PDF, work with assistant conversations | | Account | `/auth/account/export`, `/auth/account` | Export your account data, delete your account | +Additional app workflows have REST endpoints: + +| Workflow | Endpoint | +| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| PDF, JSON Resume, Reactive Resume v4/current, LinkedIn imports | `POST /resumes/file-imports` | +| PDF, Word, Markdown and JSON exports | `GET /resumes/{id}/exports/{format}`, `GET /cover-letters/{id}/exports/{format}` | +| Editor content checks and job-term matching | `GET /resumes/{id}/checks`, `POST /resumes/{id}/job-matches` | +| Public PDF readability checker | `POST /pdf-checks` | +| File uploads and deletion | `POST /files`, `DELETE /files` | +| Web search integration settings and connection tests | `/integrations/web-access` | +| Sign-in, sessions, profile, passwords, two-factor authentication, passkeys and API keys | `/api/auth/*` (outside `/api/openapi`) | + +Authentication workflows use Better Auth's native HTTP API and its own error contract. Your instance publishes their specification at `/api/auth/open-api/generate-schema`; use that specification for the enabled authentication methods. Browser interactions such as zoom, print dialogs and local undo do not require separate server endpoints: use document changes, versions and exports for the corresponding data workflows. + `GET /api/health` (outside `/api/openapi`) reports whether the instance and its database and storage are healthy. It needs no key. +## Imports and exports + +File imports accept a multipart form. The format is one of `PDF`, `REACTIVE_RESUME`, `REACTIVE_RESUME_V4`, `JSON_RESUME` or `LINKEDIN`; the maximum file size is 10 MiB. PDF imports read the text layer without AI. AI-assisted PDF and Word parsing remain available under `/ai/pdf-parses` and `/ai/docx-parses`; save the resulting data through `/resumes/imports`. + +```bash +curl "https://rxresu.me/api/openapi/resumes/file-imports" \ + -H "x-api-key: YOUR_API_KEY" \ + -F "format=JSON_RESUME" -F "file=@resume.json" + +curl "https://rxresu.me/api/openapi/resumes/RESUME_ID/exports/docx" \ + -H "x-api-key: YOUR_API_KEY" -o resume.docx +``` + +Exports accept `pdf`, `docx`, `md` or `json` and return a downloadable file. They enforce document ownership. The public checker accepts a multipart `file` (PDF, maximum 25 MB) and optional `jobDescription` text. It stores nothing, examines at most 30 pages and reports truncation or skipped checks. Parsing has a 45-second deadline. Hosting-provider request limits still apply. + +## Pagination and partial updates + +Collection endpoints support `limit` (1–100) and `offset` (zero-based). For existing array responses, omitting both preserves the complete array for compatibility. Supplying only `offset` selects a page size of 20. The `X-Total-Count` header reports the total before slicing; paginated responses also include `X-Limit` and `X-Offset`. The cover-letter list retains its existing `{ items, total }` response and default pagination. + +```bash +curl "https://rxresu.me/api/openapi/resumes?limit=20&offset=20" \ + -H "x-api-key: YOUR_API_KEY" -i +``` + +Use `PATCH` for partial application, interview, timeline-entry and cover-letter updates. Resume content uses JSON Patch at `/resumes/{id}`; metadata uses `PATCH /resumes/{id}/metadata`. Resource-oriented paths such as `/resumes/{id}/copies` and `/documents/{type}/{id}/trash` are preferred for new integrations. Existing verb-based routes, partial `PUT` routes, response shapes and enum values remain supported. + +## Retries + +State-changing operations do not implement `Idempotency-Key`. Treat mutations as unsafe to retry automatically, including requests that timed out: the change might already have happened. Read the current resource before deciding whether to repeat a write. Repeating create, copy, import or AI operations can create duplicates or incur provider charges. + ## Authentication methods The API accepts three kinds of credentials: @@ -122,9 +166,11 @@ The API accepts three kinds of credentials: 2. `Authorization: Bearer `: an OAuth access token, which MCP clients get when you connect them with OAuth. See [Using the MCP server](/guides/using-the-mcp-server). 3. The session cookie of a signed-in browser. -Send one of them. If a request has an `x-api-key` header, the API ignores any bearer token, so a wrong key isn't rescued by a valid token. +Send one credential. When several are present, the server tries API key, bearer token, then session cookie, using the first valid credential. Cookie-authenticated requests with a foreign `Origin` or cross-site fetch metadata are rejected; explicit API keys and bearer tokens do not rely on browser cookies. -A request without valid credentials gets `401` with the code `UNAUTHORIZED`. +A private request without valid credentials gets `401` with the code `UNAUTHORIZED`. Public operations are marked with `security: []` in the OpenAPI specification. Public resumes still enforce visibility, trash status and sharing passwords, and redact private data. + +REST data responses use `Cache-Control: no-store`. Uploaded profile images are public; other uploaded files require the owner's credentials, including conditional download requests. Assistant attachments are not exposed through the uploads URL. Unknown and unauthorized file URLs return `404`. ## Limits @@ -133,10 +179,12 @@ A request without valid credentials gets `401` with the code `UNAUTHORIZED`. | Requests per API key | 1,000 per hour | | Changes to one resume, document or application (create, update, patch, lock, duplicate, delete) | 300 per minute | | PDF downloads of one resume (`GET /resumes/{id}/pdf`) | 5 per minute | +| Public PDF checks per client address | 5 per minute | +| REST request body size (before endpoint-specific limits) | 40 MiB | | AI requests | 20 per minute | | Request body size on Vercel installations | 4.5 MB (see [Large RPC requests](/guides/large-rpc-requests)) | -When you hit a limit, the API responds with `429`. On a self-hosted installation, `FLAG_DISABLE_API_RATE_LIMIT` turns off the per-key limit together with the sign-in limits; the other limits stay on (see [Environment variables](/self-hosting/environment-variables)). +Procedure limits apply in production. When you hit a limit, the API responds with `429`. On a self-hosted installation, `FLAG_DISABLE_API_RATE_LIMIT` turns off the per-key limit together with the sign-in limits; the procedure limits also turn off (see [Environment variables](/self-hosting/environment-variables)). ## Errors @@ -146,7 +194,7 @@ Errors come back as JSON with a machine-readable `code`, the HTTP `status` and a { "defined": false, "code": "NOT_FOUND", "status": 404, "message": "Not Found" } ``` -Branch on `code` rather than on the message text. +Branch on `code` rather than on the message text. Invalid input uses `400`, missing authentication `401`, forbidden actions `403`, missing or inaccessible resources `404`, conflicts `409`, oversized requests `413`, rate limits `429`, and unexpected failures `500`. Unexpected failures do not expose internal exception details. ## Revoke a key diff --git a/docs/spec.json b/docs/spec.json index 372cb17f9..dc6dc6450 100644 --- a/docs/spec.json +++ b/docs/spec.json @@ -2,7 +2,7 @@ "info": { "title": "Reactive Resume", "version": "6.0.0", - "description": "Reactive Resume API", + "description": "Reactive Resume API. Mutations do not support Idempotency-Key. Do not automatically retry POST, PUT, PATCH or DELETE requests after a timeout: first read the resource to determine whether the operation succeeded. Existing enum values and response shapes are retained for compatibility.", "license": { "name": "MIT", "url": "https://github.com/reactive-resume/reactive-resume/blob/main/LICENSE" @@ -271,12 +271,4228 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the summary heading, icon, and decoration while retaining summary content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the summary is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the summary always begins on a new page." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "content" + ], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ] + }, + "description": "The items to display in the profiles section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the projects section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the projects of the author." + }, + "skills": { + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ] + }, + "description": "The items to display in the languages section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ] + }, + "description": "The items to display in the interests section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the awards section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the certifications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the publications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ] + }, + "description": "The items to display in the references section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "cover-letter", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": [ + "id", + "hidden", + "recipient", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "summary", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "profiles", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "experience", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "education", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "projects", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "enum": [ + "inline", + "list" + ], + "type": "string", + "default": "inline", + "description": "How skill keywords are displayed: inline separated by commas, or one bullet per keyword." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "skills", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the skill. Empty while the entry is a draft, which isn't printed." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "proficiency", + "level", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "languages", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "interests", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "awards", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "certifications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "publications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "volunteer", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "references", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + } + ] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "sidebarSide": { + "enum": [ + "left", + "right" + ], + "type": "string", + "description": "Which side of the page the sidebar column sits on in two-column templates. When unset, each template uses its own side (and right-to-left resumes mirror it)." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": [ + "fullWidth", + "main", + "sidebar" + ] + }, + "description": "The pages to display in the layout." + } + }, + "required": [ + "sidebarWidth", + "pages" + ], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "layout", + "page", + "design", + "typography", + "notes" + ], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." } - ] + }, + "required": [ + "picture", + "basics", + "summary", + "sections", + "customSections", + "metadata" + ], + "additionalProperties": {} } } } @@ -400,8 +4616,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/ai/parse-docx": { @@ -460,12 +4718,4228 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the summary heading, icon, and decoration while retaining summary content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the summary is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the summary always begins on a new page." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "content" + ], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ] + }, + "description": "The items to display in the profiles section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the projects section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the projects of the author." + }, + "skills": { + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ] + }, + "description": "The items to display in the languages section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ] + }, + "description": "The items to display in the interests section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the awards section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the certifications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the publications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ] + }, + "description": "The items to display in the references section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "cover-letter", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": [ + "id", + "hidden", + "recipient", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "summary", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "profiles", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "experience", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "education", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "projects", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "enum": [ + "inline", + "list" + ], + "type": "string", + "default": "inline", + "description": "How skill keywords are displayed: inline separated by commas, or one bullet per keyword." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "skills", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the skill. Empty while the entry is a draft, which isn't printed." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "proficiency", + "level", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "languages", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "interests", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "awards", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "certifications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "publications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "volunteer", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "references", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + } + ] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "sidebarSide": { + "enum": [ + "left", + "right" + ], + "type": "string", + "description": "Which side of the page the sidebar column sits on in two-column templates. When unset, each template uses its own side (and right-to-left resumes mirror it)." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": [ + "fullWidth", + "main", + "sidebar" + ] + }, + "description": "The pages to display in the layout." + } + }, + "required": [ + "sidebarWidth", + "pages" + ], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "layout", + "page", + "design", + "typography", + "notes" + ], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." } - ] + }, + "required": [ + "picture", + "basics", + "summary", + "sections", + "customSections", + "metadata" + ], + "additionalProperties": {} } } } @@ -589,8 +9063,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/ai/ats-review": { @@ -847,8 +9363,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/ai/improve": { @@ -1053,8 +9611,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/ai-providers": { @@ -1065,18 +9665,180 @@ "tags": [ "AI Providers" ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true + } + ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} - } - ] + "type": "array", + "items": { + "type": "object", + "properties": { + "managed": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "provider": { + "enum": [ + "openai", + "anthropic", + "gemini", + "vercel-ai-gateway", + "openrouter", + "mistral", + "cohere", + "xai", + "groq", + "deepseek", + "togetherai", + "fireworks", + "cerebras", + "perplexity", + "ollama", + "openai-compatible" + ], + "type": "string" + }, + "model": { + "type": "string" + }, + "baseURL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enabled": { + "type": "boolean" + }, + "testStatus": { + "type": "string" + }, + "testError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "apiKeyPreview": { + "type": "string" + }, + "apiKeyFingerprint": { + "type": "string" + }, + "lastTestedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "lastUsedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "managed", + "id", + "label", + "provider", + "model", + "baseURL", + "enabled", + "testStatus", + "testError", + "apiKeyPreview", + "apiKeyFingerprint", + "lastTestedAt", + "lastUsedAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" } } } @@ -1140,8 +9902,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "post": { "operationId": "createAiProvider", @@ -1209,11 +10013,124 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "managed": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "provider": { + "enum": [ + "openai", + "anthropic", + "gemini", + "vercel-ai-gateway", + "openrouter", + "mistral", + "cohere", + "xai", + "groq", + "deepseek", + "togetherai", + "fireworks", + "cerebras", + "perplexity", + "ollama", + "openai-compatible" + ], + "type": "string" + }, + "model": { + "type": "string" + }, + "baseURL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enabled": { + "type": "boolean" + }, + "testStatus": { + "type": "string" + }, + "testError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "apiKeyPreview": { + "type": "string" + }, + "apiKeyFingerprint": { + "type": "string" + }, + "lastTestedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "lastUsedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" } + }, + "required": [ + "managed", + "id", + "label", + "provider", + "model", + "baseURL", + "enabled", + "testStatus", + "testError", + "apiKeyPreview", + "apiKeyFingerprint", + "lastTestedAt", + "lastUsedAt", + "createdAt", + "updatedAt" ] } } @@ -1398,8 +10315,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/ai-providers/{id}": { @@ -1476,11 +10435,124 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "managed": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "provider": { + "enum": [ + "openai", + "anthropic", + "gemini", + "vercel-ai-gateway", + "openrouter", + "mistral", + "cohere", + "xai", + "groq", + "deepseek", + "togetherai", + "fireworks", + "cerebras", + "perplexity", + "ollama", + "openai-compatible" + ], + "type": "string" + }, + "model": { + "type": "string" + }, + "baseURL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enabled": { + "type": "boolean" + }, + "testStatus": { + "type": "string" + }, + "testError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "apiKeyPreview": { + "type": "string" + }, + "apiKeyFingerprint": { + "type": "string" + }, + "lastTestedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "lastUsedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" } + }, + "required": [ + "managed", + "id", + "label", + "provider", + "model", + "baseURL", + "enabled", + "testStatus", + "testError", + "apiKeyPreview", + "apiKeyFingerprint", + "lastTestedAt", + "lastUsedAt", + "createdAt", + "updatedAt" ] } } @@ -1725,8 +10797,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteAiProvider", @@ -1747,21 +10861,7 @@ ], "responses": { "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "anyOf": [ - { - "not": {} - }, - { - "not": {} - } - ] - } - } - } + "description": "OK" }, "403": { "description": "403", @@ -1882,8 +10982,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/ai-providers/{id}/test": { @@ -1910,11 +11052,124 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "managed": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "provider": { + "enum": [ + "openai", + "anthropic", + "gemini", + "vercel-ai-gateway", + "openrouter", + "mistral", + "cohere", + "xai", + "groq", + "deepseek", + "togetherai", + "fireworks", + "cerebras", + "perplexity", + "ollama", + "openai-compatible" + ], + "type": "string" + }, + "model": { + "type": "string" + }, + "baseURL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enabled": { + "type": "boolean" + }, + "testStatus": { + "type": "string" + }, + "testError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "apiKeyPreview": { + "type": "string" + }, + "apiKeyFingerprint": { + "type": "string" + }, + "lastTestedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "lastUsedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" } + }, + "required": [ + "managed", + "id", + "label", + "provider", + "model", + "baseURL", + "enabled", + "testStatus", + "testError", + "apiKeyPreview", + "apiKeyFingerprint", + "lastTestedAt", + "lastUsedAt", + "createdAt", + "updatedAt" ] } } @@ -2219,8 +11474,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/threads": { @@ -2230,23 +11527,265 @@ "tags": [ "Agent" ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true + } + ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} - } - ] + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workingResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "aiProviderId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "editsProposed": { + "type": "number" + }, + "editsAccepted": { + "type": "number" + }, + "activeRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastMessageAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "archivedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "deletedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "title", + "status", + "sourceResumeId", + "workingResumeId", + "coverLetterId", + "aiProviderId", + "resumeName", + "coverLetterName", + "providerLabel", + "editsProposed", + "editsAccepted", + "activeRunId", + "lastMessageAt", + "archivedAt", + "deletedAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "post": { "operationId": "startAgentThread", @@ -2284,17 +11823,210 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workingResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "aiProviderId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "editsProposed": { + "type": "number" + }, + "editsAccepted": { + "type": "number" + }, + "activeRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastMessageAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "archivedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "deletedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" } + }, + "required": [ + "id", + "title", + "status", + "sourceResumeId", + "workingResumeId", + "coverLetterId", + "aiProviderId", + "resumeName", + "coverLetterName", + "providerLabel", + "editsProposed", + "editsAccepted", + "activeRunId", + "lastMessageAt", + "archivedAt", + "deletedAt", + "createdAt", + "updatedAt" ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/threads/{id}": { @@ -2320,17 +12052,346 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "thread": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workingResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "aiProviderId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "editsProposed": { + "type": "number" + }, + "editsAccepted": { + "type": "number" + }, + "activeRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastMessageAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "archivedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "deletedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "title", + "status", + "sourceResumeId", + "workingResumeId", + "coverLetterId", + "aiProviderId", + "resumeName", + "coverLetterName", + "providerLabel", + "editsProposed", + "editsAccepted", + "activeRunId", + "lastMessageAt", + "archivedAt", + "deletedAt", + "createdAt", + "updatedAt" + ] + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "role": { + "enum": [ + "system", + "user", + "assistant" + ], + "type": "string" + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": {} + } + }, + "metadata": {} + }, + "required": [ + "id", + "role", + "parts" + ] + } + }, + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "messageId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "filename": { + "type": "string" + }, + "mediaType": { + "type": "string" + }, + "size": { + "type": "number" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "threadId", + "messageId", + "filename", + "mediaType", + "size", + "createdAt" + ] + } + }, + "document": { + "anyOf": [ + { + "type": "object", + "properties": { + "kind": { + "enum": [ + "resume", + "letter" + ], + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "locked": { + "type": "boolean" + } + }, + "required": [ + "kind", + "id", + "name", + "locked" + ] + }, + { + "type": "null" + } + ] + }, + "isReadOnly": { + "type": "boolean" } + }, + "required": [ + "thread", + "messages", + "attachments", + "document", + "isReadOnly" ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "patch": { "operationId": "updateAgentThread", @@ -2373,17 +12434,210 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workingResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "aiProviderId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "providerLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "editsProposed": { + "type": "number" + }, + "editsAccepted": { + "type": "number" + }, + "activeRunId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastMessageAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "archivedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "deletedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" } + }, + "required": [ + "id", + "title", + "status", + "sourceResumeId", + "workingResumeId", + "coverLetterId", + "aiProviderId", + "resumeName", + "coverLetterName", + "providerLabel", + "editsProposed", + "editsAccepted", + "activeRunId", + "lastMessageAt", + "archivedAt", + "deletedAt", + "createdAt", + "updatedAt" ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteAgentThread", @@ -2403,23 +12657,51 @@ ], "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/messages/send": { @@ -2439,7 +12721,43 @@ "threadId": { "type": "string" }, - "message": {}, + "message": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "role": { + "enum": [ + "system", + "user", + "assistant" + ], + "type": "string" + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": {} + } + }, + "metadata": {} + }, + "required": [ + "id", + "role", + "parts" + ] + }, "attachmentIds": { "type": "array", "maxItems": 10, @@ -2479,19 +12797,114 @@ "200": { "description": "OK", "content": { - "application/json": { + "text/event-stream": { "schema": { - "anyOf": [ - {}, + "oneOf": [ { - "not": {} + "type": "object", + "properties": { + "event": { + "const": "message" + }, + "data": { + "type": "string" + }, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event", + "data" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "done" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "error" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] } ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/messages/stop": { @@ -2511,7 +12924,43 @@ "threadId": { "type": "string" }, - "partialMessage": {} + "partialMessage": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "role": { + "enum": [ + "system", + "user", + "assistant" + ], + "type": "string" + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": {} + } + }, + "metadata": {} + }, + "required": [ + "id", + "role", + "parts" + ] + } }, "required": [ "threadId" @@ -2522,23 +12971,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/messages/resume": { @@ -2564,19 +13041,114 @@ "200": { "description": "OK", "content": { - "application/json": { + "text/event-stream": { "schema": { - "anyOf": [ - {}, + "oneOf": [ { - "not": {} + "type": "object", + "properties": { + "event": { + "const": "message" + }, + "data": { + "type": "string" + }, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event", + "data" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "done" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "error" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] } ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/messages/edit-status": { @@ -2661,8 +13233,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/attachments": { @@ -2692,7 +13306,9 @@ }, "data": { "type": "string", - "minLength": 1 + "minLength": 1, + "maxLength": 34952536, + "contentEncoding": "base64" } }, "required": [ @@ -2711,17 +13327,95 @@ "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "messageId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "filename": { + "type": "string" + }, + "mediaType": { + "type": "string" + }, + "size": { + "type": "number" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" } + }, + "required": [ + "id", + "threadId", + "messageId", + "filename", + "mediaType", + "size", + "createdAt" ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/agent/attachments/{id}": { @@ -2743,23 +13437,51 @@ ], "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications": { @@ -2771,6 +13493,30 @@ "Applications" ], "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true + }, { "name": "status", "in": "query", @@ -3355,9 +14101,71 @@ } } } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "post": { "operationId": "createApplication", @@ -3701,8 +14509,269 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "patch": { + "operationId": "patchApplications", + "summary": "Bulk update applications", + "description": "Applies the same change (move stage, archive/unarchive, add tags) to multiple applications at once. Requires authentication.", + "tags": [ + "Applications" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "minItems": 1, + "maxItems": 200, + "items": { + "type": "string" + } + }, + "status": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string" + }, + "closedReason": { + "anyOf": [ + { + "enum": [ + "not-selected", + "withdrew", + "accepted-other", + "no-response" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "addTags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "ids" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The number of applications updated.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "updated": { + "type": "number" + } + }, + "required": [ + "updated" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "delete": { + "operationId": "deleteApplications", + "summary": "Bulk delete applications", + "description": "Permanently deletes multiple applications at once. Requires authentication.", + "tags": [ + "Applications" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "minItems": 1, + "maxItems": 200, + "items": { + "type": "string" + } + } + }, + "required": [ + "ids" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The number of applications deleted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deleted": { + "type": "number" + } + }, + "required": [ + "deleted" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}": { @@ -4274,8 +15343,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "put": { "operationId": "updateApplication", @@ -5165,8 +16276,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteApplication", @@ -5188,23 +16341,984 @@ ], "responses": { "200": { - "description": "The application was deleted successfully.", + "description": "The application was deleted successfully." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "patch": { + "operationId": "patchApplication", + "summary": "Update a job application", + "description": "Updates one or more fields of an application, including moving it to a different pipeline stage or archiving it. Moving stages automatically appends an entry to the activity timeline. Only provided fields are changed. Requires authentication.", + "tags": [ + "Applications" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "company": { + "type": "string", + "minLength": 1, + "description": "The company applied to." + }, + "role": { + "type": "string", + "minLength": 1, + "description": "The role / job title." + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "salary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "jobDescription": { + "anyOf": [ + { + "type": "string", + "maxLength": 20000 + }, + { + "type": "null" + } + ] + }, + "postingSource": { + "anyOf": [ + { + "type": "object", + "properties": { + "method": { + "enum": [ + "paste", + "builtin", + "firecrawl", + "tavily", + "exa" + ], + "type": "string" + }, + "format": { + "enum": [ + "text", + "markdown" + ], + "type": "string" + }, + "requestedUrl": { + "type": "string", + "format": "uri" + }, + "resolvedUrl": { + "type": "string", + "format": "uri" + }, + "retrievedAt": { + "type": "string", + "format": "date-time" + }, + "providerFetchedAt": { + "type": "string", + "format": "date-time" + }, + "truncated": { + "type": "boolean" + }, + "completeness": { + "enum": [ + "unknown", + "incomplete" + ], + "type": "string" + }, + "fallbackReason": { + "enum": [ + "unsafe-url", + "unreachable", + "not-a-page", + "too-large", + "auth", + "quota", + "malformed", + "empty", + "challenge", + "timeout", + "unavailable", + "rate-limit" + ], + "type": "string" + } + }, + "required": [ + "method", + "format", + "truncated", + "completeness" + ] + }, + { + "type": "null" + } + ] + }, + "notes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "followUpAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "followUpNote": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + }, + "email": { + "type": "string", + "default": "" + }, + "phone": { + "type": "string", + "default": "" + } + }, + "required": [ + "name" + ] + } + }, + "resumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked Reactive Resume, if any." + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked saved cover letter, if any." + }, + "requirements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What the posting asks for, as read when the application was added." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string" + }, + "stageEnteredAt": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + "closedReason": { + "anyOf": [ + { + "enum": [ + "not-selected", + "withdrew", + "accepted-other", + "no-response" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Why the application closed; kept when status stays closed, cleared by any other stage." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated application.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the application." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The company applied to." + }, + "role": { + "type": "string", + "minLength": 1, + "description": "The role / job title." + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "salary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string", + "description": "The current pipeline stage. `closed` ends it, with a reason." + }, + "closedReason": { + "anyOf": [ + { + "enum": [ + "not-selected", + "withdrew", + "accepted-other", + "no-response" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Why a closed application ended: not-selected, withdrew, accepted-other or no-response." + }, + "resumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked Reactive Resume, if any." + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked saved cover letter, if any." + }, + "sentResumeVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked resume saved when the application was sent (reached Applied)." + }, + "sentCheckScore": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ], + "description": "The resume's Check score when it was sent, out of 100." + }, + "sentCoverLetterVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked letter saved when the application was sent." + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "sourceUrl": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "jobDescription": { + "anyOf": [ + { + "type": "string", + "maxLength": 20000 + }, + { + "type": "null" + } + ] + }, + "postingSource": { + "anyOf": [ + { + "type": "object", + "properties": { + "method": { + "enum": [ + "paste", + "builtin", + "firecrawl", + "tavily", + "exa" + ], + "type": "string" + }, + "format": { + "enum": [ + "text", + "markdown" + ], + "type": "string" + }, + "requestedUrl": { + "type": "string", + "format": "uri" + }, + "resolvedUrl": { + "type": "string", + "format": "uri" + }, + "retrievedAt": { + "type": "string", + "format": "date-time" + }, + "providerFetchedAt": { + "type": "string", + "format": "date-time" + }, + "truncated": { + "type": "boolean" + }, + "completeness": { + "enum": [ + "unknown", + "incomplete" + ], + "type": "string" + }, + "fallbackReason": { + "enum": [ + "unsafe-url", + "unreachable", + "not-a-page", + "too-large", + "auth", + "quota", + "malformed", + "empty", + "challenge", + "timeout", + "unavailable", + "rate-limit" + ], + "type": "string" + } + }, + "required": [ + "method", + "format", + "truncated", + "completeness" + ] + }, + { + "type": "null" + } + ] + }, + "requirements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What the posting asks for, as read when the application was added." + }, + "matchScore": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + { + "type": "null" + } + ] + }, + "aiMetadata": { + "anyOf": [ + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "null" + } + ] + }, + "notes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "followUpAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "followUpNote": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + }, + "email": { + "type": "string", + "default": "" + }, + "phone": { + "type": "string", + "default": "" + } + }, + "required": [ + "name" + ] + } + }, + "activity": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "stage" + }, + "stage": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string" + } + }, + "required": [ + "id", + "at", + "type", + "stage" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "note" + }, + "text": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id", + "at", + "type", + "text" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "interview" + }, + "kind": { + "enum": [ + "screening", + "technical", + "behavioral", + "onsite", + "other" + ], + "type": "string" + }, + "durationMinutes": { + "type": "integer", + "minimum": 5, + "maximum": 1440, + "default": 60 + }, + "location": { + "type": "string", + "maxLength": 500, + "default": "" + }, + "notes": { + "type": "string", + "maxLength": 5000, + "default": "" + } + }, + "required": [ + "id", + "at", + "type", + "kind" + ] + } + ] + } + }, + "appliedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "company", + "role", + "location", + "salary", + "status", + "closedReason", + "resumeId", + "coverLetterId", + "sentResumeVersionId", + "sentCheckScore", + "sentCoverLetterVersionId", + "source", + "tags", + "sourceUrl", + "jobDescription", + "postingSource", + "requirements", + "matchScore", + "aiMetadata", + "notes", + "resumeFileUrl", + "resumeFileName", + "coverLetterUrl", + "coverLetterName", + "followUpAt", + "followUpNote", + "contacts", + "activity", + "appliedAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/import": { @@ -5570,8 +17684,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/documents/{kind}": { @@ -6173,6 +18329,37 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } } }, @@ -6755,8 +18942,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/notes": { @@ -7350,8 +19579,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/interviews": { @@ -7969,8 +20240,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/interviews/{entryId}": { @@ -8589,8 +20902,710 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "patch": { + "operationId": "patchApplicationInterview", + "summary": "Update a scheduled interview", + "description": "Updates an interview timeline entry (date-time, kind, duration, location, notes). Only provided fields are changed. Delete interviews with the timeline entry delete endpoint. Requires authentication.", + "tags": [ + "Applications" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "entryId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "at": { + "type": "string", + "format": "date-time", + "description": "Scheduled start, as an ISO 8601 date-time." + }, + "kind": { + "enum": [ + "screening", + "technical", + "behavioral", + "onsite", + "other" + ], + "type": "string" + }, + "durationMinutes": { + "type": "integer", + "minimum": 5, + "maximum": 1440 + }, + "location": { + "type": "string", + "maxLength": 500 + }, + "notes": { + "type": "string", + "maxLength": 5000 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated application.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the application." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The company applied to." + }, + "role": { + "type": "string", + "minLength": 1, + "description": "The role / job title." + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "salary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string", + "description": "The current pipeline stage. `closed` ends it, with a reason." + }, + "closedReason": { + "anyOf": [ + { + "enum": [ + "not-selected", + "withdrew", + "accepted-other", + "no-response" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Why a closed application ended: not-selected, withdrew, accepted-other or no-response." + }, + "resumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked Reactive Resume, if any." + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked saved cover letter, if any." + }, + "sentResumeVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked resume saved when the application was sent (reached Applied)." + }, + "sentCheckScore": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ], + "description": "The resume's Check score when it was sent, out of 100." + }, + "sentCoverLetterVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked letter saved when the application was sent." + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "sourceUrl": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "jobDescription": { + "anyOf": [ + { + "type": "string", + "maxLength": 20000 + }, + { + "type": "null" + } + ] + }, + "postingSource": { + "anyOf": [ + { + "type": "object", + "properties": { + "method": { + "enum": [ + "paste", + "builtin", + "firecrawl", + "tavily", + "exa" + ], + "type": "string" + }, + "format": { + "enum": [ + "text", + "markdown" + ], + "type": "string" + }, + "requestedUrl": { + "type": "string", + "format": "uri" + }, + "resolvedUrl": { + "type": "string", + "format": "uri" + }, + "retrievedAt": { + "type": "string", + "format": "date-time" + }, + "providerFetchedAt": { + "type": "string", + "format": "date-time" + }, + "truncated": { + "type": "boolean" + }, + "completeness": { + "enum": [ + "unknown", + "incomplete" + ], + "type": "string" + }, + "fallbackReason": { + "enum": [ + "unsafe-url", + "unreachable", + "not-a-page", + "too-large", + "auth", + "quota", + "malformed", + "empty", + "challenge", + "timeout", + "unavailable", + "rate-limit" + ], + "type": "string" + } + }, + "required": [ + "method", + "format", + "truncated", + "completeness" + ] + }, + { + "type": "null" + } + ] + }, + "requirements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What the posting asks for, as read when the application was added." + }, + "matchScore": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + { + "type": "null" + } + ] + }, + "aiMetadata": { + "anyOf": [ + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "null" + } + ] + }, + "notes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "followUpAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "followUpNote": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + }, + "email": { + "type": "string", + "default": "" + }, + "phone": { + "type": "string", + "default": "" + } + }, + "required": [ + "name" + ] + } + }, + "activity": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "stage" + }, + "stage": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string" + } + }, + "required": [ + "id", + "at", + "type", + "stage" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "note" + }, + "text": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id", + "at", + "type", + "text" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "interview" + }, + "kind": { + "enum": [ + "screening", + "technical", + "behavioral", + "onsite", + "other" + ], + "type": "string" + }, + "durationMinutes": { + "type": "integer", + "minimum": 5, + "maximum": 1440, + "default": 60 + }, + "location": { + "type": "string", + "maxLength": 500, + "default": "" + }, + "notes": { + "type": "string", + "maxLength": 5000, + "default": "" + } + }, + "required": [ + "id", + "at", + "type", + "kind" + ] + } + ] + } + }, + "appliedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "company", + "role", + "location", + "salary", + "status", + "closedReason", + "resumeId", + "coverLetterId", + "sentResumeVersionId", + "sentCheckScore", + "sentCoverLetterVersionId", + "source", + "tags", + "sourceUrl", + "jobDescription", + "postingSource", + "requirements", + "matchScore", + "aiMetadata", + "notes", + "resumeFileUrl", + "resumeFileName", + "coverLetterUrl", + "coverLetterName", + "followUpAt", + "followUpNote", + "contacts", + "activity", + "appliedAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/timeline/{entryId}": { @@ -9189,8 +22204,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteApplicationTimelineEntry", @@ -9767,8 +22824,690 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "patch": { + "operationId": "patchApplicationTimelineEntry", + "summary": "Update a timeline entry", + "description": "Updates a stage or note timeline entry date, or note text for note entries. Interview entries are rejected; use updateApplicationInterview. Requires authentication.", + "tags": [ + "Applications" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "entryId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "date": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + "text": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated application.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the application." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The company applied to." + }, + "role": { + "type": "string", + "minLength": 1, + "description": "The role / job title." + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "salary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string", + "description": "The current pipeline stage. `closed` ends it, with a reason." + }, + "closedReason": { + "anyOf": [ + { + "enum": [ + "not-selected", + "withdrew", + "accepted-other", + "no-response" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Why a closed application ended: not-selected, withdrew, accepted-other or no-response." + }, + "resumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked Reactive Resume, if any." + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked saved cover letter, if any." + }, + "sentResumeVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked resume saved when the application was sent (reached Applied)." + }, + "sentCheckScore": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ], + "description": "The resume's Check score when it was sent, out of 100." + }, + "sentCoverLetterVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked letter saved when the application was sent." + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "sourceUrl": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "jobDescription": { + "anyOf": [ + { + "type": "string", + "maxLength": 20000 + }, + { + "type": "null" + } + ] + }, + "postingSource": { + "anyOf": [ + { + "type": "object", + "properties": { + "method": { + "enum": [ + "paste", + "builtin", + "firecrawl", + "tavily", + "exa" + ], + "type": "string" + }, + "format": { + "enum": [ + "text", + "markdown" + ], + "type": "string" + }, + "requestedUrl": { + "type": "string", + "format": "uri" + }, + "resolvedUrl": { + "type": "string", + "format": "uri" + }, + "retrievedAt": { + "type": "string", + "format": "date-time" + }, + "providerFetchedAt": { + "type": "string", + "format": "date-time" + }, + "truncated": { + "type": "boolean" + }, + "completeness": { + "enum": [ + "unknown", + "incomplete" + ], + "type": "string" + }, + "fallbackReason": { + "enum": [ + "unsafe-url", + "unreachable", + "not-a-page", + "too-large", + "auth", + "quota", + "malformed", + "empty", + "challenge", + "timeout", + "unavailable", + "rate-limit" + ], + "type": "string" + } + }, + "required": [ + "method", + "format", + "truncated", + "completeness" + ] + }, + { + "type": "null" + } + ] + }, + "requirements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What the posting asks for, as read when the application was added." + }, + "matchScore": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + { + "type": "null" + } + ] + }, + "aiMetadata": { + "anyOf": [ + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "null" + } + ] + }, + "notes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "followUpAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "followUpNote": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + }, + "email": { + "type": "string", + "default": "" + }, + "phone": { + "type": "string", + "default": "" + } + }, + "required": [ + "name" + ] + } + }, + "activity": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "stage" + }, + "stage": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string" + } + }, + "required": [ + "id", + "at", + "type", + "stage" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "note" + }, + "text": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id", + "at", + "type", + "text" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "interview" + }, + "kind": { + "enum": [ + "screening", + "technical", + "behavioral", + "onsite", + "other" + ], + "type": "string" + }, + "durationMinutes": { + "type": "integer", + "minimum": 5, + "maximum": 1440, + "default": 60 + }, + "location": { + "type": "string", + "maxLength": 500, + "default": "" + }, + "notes": { + "type": "string", + "maxLength": 5000, + "default": "" + } + }, + "required": [ + "id", + "at", + "type", + "kind" + ] + } + ] + } + }, + "appliedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "company", + "role", + "location", + "salary", + "status", + "closedReason", + "resumeId", + "coverLetterId", + "sentResumeVersionId", + "sentCheckScore", + "sentCoverLetterVersionId", + "source", + "tags", + "sourceUrl", + "jobDescription", + "postingSource", + "requirements", + "matchScore", + "aiMetadata", + "notes", + "resumeFileUrl", + "resumeFileName", + "coverLetterUrl", + "coverLetterName", + "followUpAt", + "followUpNote", + "contacts", + "activity", + "appliedAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/bulk-update": { @@ -9853,8 +23592,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/bulk-delete": { @@ -9906,8 +23687,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/stats": { @@ -9983,8 +23806,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/tags": { @@ -9995,6 +23860,32 @@ "tags": [ "Applications" ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true + } + ], "responses": { "200": { "description": "Distinct tags.", @@ -10007,9 +23898,71 @@ } } } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/ai/search-postings": { @@ -10252,8 +24205,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/ai/parse-posting": { @@ -10676,8 +24671,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/ai/autofill": { @@ -10857,8 +24894,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/ai/match-score": { @@ -11013,8 +25092,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/ai/draft-message": { @@ -11197,8 +25318,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/applications/{id}/ai/tailor-resume": { @@ -11360,8 +25523,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/auth/providers": { @@ -11372,23 +25577,65 @@ "tags": [ "Authentication" ], + "parameters": [], "responses": { "200": { "description": "A map of enabled authentication provider identifiers to their display names.", "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} - } - ] + "type": "object", + "propertyNames": { + "enum": [ + "credential", + "passkey", + "google", + "github", + "linkedin", + "custom" + ], + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [] } }, "/auth/account/export": { @@ -11399,23 +25646,5496 @@ "tags": [ "Authentication" ], + "parameters": [], "responses": { "200": { "description": "The user's exported account data.", "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} + "type": "object", + "properties": { + "exportedAt": { + "type": "string" + }, + "user": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "username": { + "type": "string" + }, + "displayUsername": { + "type": "string" + }, + "image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "emailVerified": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "name", + "email", + "username", + "displayUsername", + "image", + "emailVerified", + "createdAt", + "updatedAt" + ] + }, + "resumes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The tags of the resume." + }, + "isPublic": { + "type": "boolean", + "description": "Whether the resume is public." + }, + "showDownloadButtons": { + "type": "boolean", + "description": "Whether download buttons are shown on the public resume page." + }, + "isLocked": { + "type": "boolean", + "description": "Whether the resume is locked." + }, + "data": { + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the summary heading, icon, and decoration while retaining summary content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the summary is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the summary always begins on a new page." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "content" + ], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ] + }, + "description": "The items to display in the profiles section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": {}, + "end": {}, + "present": {}, + "raw": {} + }, + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the projects section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the projects of the author." + }, + "skills": { + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ] + }, + "description": "The items to display in the languages section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ] + }, + "description": "The items to display in the interests section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the awards section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the certifications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the publications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ] + }, + "description": "The items to display in the references section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "cover-letter", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": [ + "id", + "hidden", + "recipient", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "summary", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "profiles", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "experience", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": {}, + "position": {}, + "period": {}, + "dates": {}, + "description": {} + } + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "education", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "projects", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "enum": [ + "inline", + "list" + ], + "type": "string", + "default": "inline", + "description": "How skill keywords are displayed: inline separated by commas, or one bullet per keyword." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "skills", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the skill. Empty while the entry is a draft, which isn't printed." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "proficiency", + "level", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "languages", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "interests", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "awards", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "certifications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "publications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "volunteer", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "references", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + } + ] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "sidebarSide": { + "enum": [ + "left", + "right" + ], + "type": "string", + "description": "Which side of the page the sidebar column sits on in two-column templates. When unset, each template uses its own side (and right-to-left resumes mirror it)." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": [ + "fullWidth", + "main", + "sidebar" + ] + }, + "description": "The pages to display in the layout." + } + }, + "required": [ + "sidebarWidth", + "pages" + ], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "layout", + "page", + "design", + "typography", + "notes" + ], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + } + }, + "required": [ + "picture", + "basics", + "summary", + "sections", + "customSections", + "metadata" + ], + "additionalProperties": {} + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was created." + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was last updated." + } + }, + "required": [ + "id", + "name", + "slug", + "tags", + "isPublic", + "showDownloadButtons", + "isLocked", + "data", + "createdAt", + "updatedAt" + ] + } + }, + "coverLetters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "style": { + "type": "object", + "properties": { + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ] + }, + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ] + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "page", + "design", + "typography" + ] + }, + "sectionId": { + "type": "string", + "minLength": 1 + }, + "itemId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "basics", + "picture", + "metadata", + "sectionId", + "itemId" + ] + }, + "layout": { + "enum": [ + "structured", + "freeform" + ], + "type": "string", + "description": "structured: recipient name, company and date fields, a greeting from the name, the body and a sign-off. freeform: the recipient block and body as written (letters from before structured letters).", + "default": "freeform" + }, + "recipientName": { + "type": "string", + "maxLength": 200, + "default": "", + "description": "Who the letter is to: a person, or a team." + }, + "recipientCompany": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The letter's date, as YYYY-MM-DD." + }, + "id": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceApplicationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderLinked": { + "type": "boolean", + "description": "The sender's details come live from the source resume, instead of the copy in `style`." + }, + "designLinked": { + "type": "boolean", + "description": "The design comes live from the source resume, instead of the copy in `style`." + }, + "isLocked": { + "type": "boolean", + "description": "Locked letters can't be edited or moved to Trash until they're unlocked." + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "name", + "recipient", + "content", + "style", + "id", + "sourceResumeId", + "sourceApplicationId", + "senderLinked", + "designLinked", + "isLocked", + "revision", + "createdAt", + "updatedAt" + ] + } + }, + "applications": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the application." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The company applied to." + }, + "role": { + "type": "string", + "minLength": 1, + "description": "The role / job title." + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "salary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string", + "description": "The current pipeline stage. `closed` ends it, with a reason." + }, + "closedReason": { + "anyOf": [ + { + "enum": [ + "not-selected", + "withdrew", + "accepted-other", + "no-response" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Why a closed application ended: not-selected, withdrew, accepted-other or no-response." + }, + "resumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked Reactive Resume, if any." + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked saved cover letter, if any." + }, + "sentResumeVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked resume saved when the application was sent (reached Applied)." + }, + "sentCheckScore": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ], + "description": "The resume's Check score when it was sent, out of 100." + }, + "sentCoverLetterVersionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The version of the linked letter saved when the application was sent." + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "sourceUrl": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "jobDescription": { + "anyOf": [ + { + "type": "string", + "maxLength": 20000 + }, + { + "type": "null" + } + ] + }, + "postingSource": { + "anyOf": [ + { + "type": "object", + "properties": { + "method": { + "enum": [ + "paste", + "builtin", + "firecrawl", + "tavily", + "exa" + ], + "type": "string" + }, + "format": { + "enum": [ + "text", + "markdown" + ], + "type": "string" + }, + "requestedUrl": { + "type": "string", + "format": "uri" + }, + "resolvedUrl": { + "type": "string", + "format": "uri" + }, + "retrievedAt": { + "type": "string", + "format": "date-time" + }, + "providerFetchedAt": { + "type": "string", + "format": "date-time" + }, + "truncated": { + "type": "boolean" + }, + "completeness": { + "enum": [ + "unknown", + "incomplete" + ], + "type": "string" + }, + "fallbackReason": { + "enum": [ + "unsafe-url", + "unreachable", + "not-a-page", + "too-large", + "auth", + "quota", + "malformed", + "empty", + "challenge", + "timeout", + "unavailable", + "rate-limit" + ], + "type": "string" + } + }, + "required": [ + "method", + "format", + "truncated", + "completeness" + ] + }, + { + "type": "null" + } + ] + }, + "requirements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What the posting asks for, as read when the application was added." + }, + "matchScore": { + "anyOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + { + "type": "null" + } + ] + }, + "aiMetadata": { + "anyOf": [ + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "null" + } + ] + }, + "notes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "followUpAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "followUpNote": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + }, + "email": { + "type": "string", + "default": "" + }, + "phone": { + "type": "string", + "default": "" + } + }, + "required": [ + "name" + ] + } + }, + "activity": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "stage" + }, + "stage": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string" + } + }, + "required": [ + "id", + "at", + "type", + "stage" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "note" + }, + "text": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id", + "at", + "type", + "text" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "at": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "type": { + "const": "interview" + }, + "kind": { + "enum": [ + "screening", + "technical", + "behavioral", + "onsite", + "other" + ], + "type": "string" + }, + "durationMinutes": { + "type": "integer", + "minimum": 5, + "maximum": 1440, + "default": 60 + }, + "location": { + "type": "string", + "maxLength": 500, + "default": "" + }, + "notes": { + "type": "string", + "maxLength": 5000, + "default": "" + } + }, + "required": [ + "id", + "at", + "type", + "kind" + ] + } + ] + } + }, + "appliedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "id", + "company", + "role", + "location", + "salary", + "status", + "closedReason", + "resumeId", + "coverLetterId", + "sentResumeVersionId", + "sentCheckScore", + "sentCoverLetterVersionId", + "source", + "tags", + "sourceUrl", + "jobDescription", + "postingSource", + "requirements", + "matchScore", + "aiMetadata", + "notes", + "resumeFileUrl", + "resumeFileName", + "coverLetterUrl", + "coverLetterName", + "followUpAt", + "followUpNote", + "contacts", + "activity", + "appliedAt", + "createdAt", + "updatedAt" + ] + } } + }, + "required": [ + "exportedAt", + "user", + "resumes", + "coverLetters", + "applications" ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/auth/account": { @@ -11426,23 +31146,63 @@ "tags": [ "Authentication" ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, "responses": { "200": { - "description": "The user account and all associated data have been successfully deleted.", + "description": "The user account and all associated data have been successfully deleted." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - {}, - { - "not": {} - } - ] + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters": { @@ -12159,8 +31919,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "post": { "operationId": "createCoverLetter", @@ -12892,8 +32694,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}": { @@ -13550,8 +33394,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "put": { "operationId": "updateCoverLetter", @@ -14561,8 +34447,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteCoverLetter", @@ -14604,23 +34532,1104 @@ }, "responses": { "200": { - "description": "The cover letter is in Trash.", + "description": "The cover letter is in Trash." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "patch": { + "operationId": "patchCoverLetter", + "summary": "Update a cover letter", + "description": "Updates the supplied fields of a saved cover letter using its expected revision.", + "tags": [ + "Cover Letters" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "expectedRevision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "Sets the letter's own template, which unlinks its design." + }, + "metadata": { + "type": "object", + "properties": { + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + } + }, + "description": "Sets the letter's own type, colors and page settings, which unlinks its design." + }, + "recipientName": { + "type": "string", + "maxLength": 200 + }, + "recipientCompany": { + "type": "string", + "maxLength": 200 + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ] + }, + "resumeId": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ], + "description": "The resume the letter goes with." + }, + "applicationId": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ], + "description": "The job the letter is for." + }, + "senderLinked": { + "type": "boolean", + "description": "Take the sender's details live from the resume." + }, + "designLinked": { + "type": "boolean", + "description": "Take the design live from the resume." + }, + "sessionId": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The editing session; its saves share one History version, refreshed every two minutes." + } + }, + "required": [ + "expectedRevision" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The updated cover letter.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "style": { + "type": "object", + "properties": { + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ] + }, + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ] + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "page", + "design", + "typography" + ] + }, + "sectionId": { + "type": "string", + "minLength": 1 + }, + "itemId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "basics", + "picture", + "metadata", + "sectionId", + "itemId" + ] + }, + "layout": { + "enum": [ + "structured", + "freeform" + ], + "type": "string", + "description": "structured: recipient name, company and date fields, a greeting from the name, the body and a sign-off. freeform: the recipient block and body as written (letters from before structured letters).", + "default": "freeform" + }, + "recipientName": { + "type": "string", + "maxLength": 200, + "default": "", + "description": "Who the letter is to: a person, or a team." + }, + "recipientCompany": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The letter's date, as YYYY-MM-DD." + }, + "id": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceApplicationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderLinked": { + "type": "boolean", + "description": "The sender's details come live from the source resume, instead of the copy in `style`." + }, + "designLinked": { + "type": "boolean", + "description": "The design comes live from the source resume, instead of the copy in `style`." + }, + "isLocked": { + "type": "boolean", + "description": "Locked letters can't be edited or moved to Trash until they're unlocked." + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "name", + "recipient", + "content", + "style", + "id", + "sourceResumeId", + "sourceApplicationId", + "senderLinked", + "designLinked", + "isLocked", + "revision", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}/refresh-style": { @@ -15302,8 +36311,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}/duplicate": { @@ -15977,8 +37028,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}/export": { @@ -16584,8 +37677,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/import": { @@ -17826,8 +38961,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}/versions": { @@ -17847,6 +39024,30 @@ "type": "string", "minLength": 1 } + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true } ], "responses": { @@ -17900,9 +39101,71 @@ } } } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "post": { "operationId": "createCoverLetterVersion", @@ -17991,8 +39254,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}/versions/{versionId}": { @@ -18633,8 +39938,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "patch": { "operationId": "renameCoverLetterVersion", @@ -18732,8 +40079,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteCoverLetterVersion", @@ -18764,23 +40153,51 @@ ], "responses": { "200": { - "description": "The version was deleted.", + "description": "The version was deleted." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}/versions/{versionId}/restore": { @@ -19446,8 +40863,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/cover-letters/{id}/draft": { @@ -19565,8 +41024,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents": { @@ -19578,6 +41079,30 @@ "Documents" ], "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true + }, { "name": "trashed", "in": "query", @@ -19688,9 +41213,71 @@ } } } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/counts": { @@ -19701,6 +41288,7 @@ "tags": [ "Documents" ], + "parameters": [], "responses": { "200": { "description": "OK", @@ -19727,8 +41315,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/rename": { @@ -19776,23 +41406,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/tags": { @@ -19844,23 +41502,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/lock": { @@ -19906,23 +41592,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/application": { @@ -19977,23 +41691,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/trash": { @@ -20035,23 +41777,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/restore": { @@ -20093,23 +41863,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/purge": { @@ -20151,23 +41949,51 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/documents/copy-for-job": { @@ -20220,8 +42046,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/flags": { @@ -20232,6 +42100,7 @@ "tags": [ "Feature Flags" ], + "parameters": [], "responses": { "200": { "description": "The current feature flags for this instance.", @@ -20261,8 +42130,40 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [] } }, "/resumes/tags": { @@ -20273,6 +42174,32 @@ "tags": [ "Resumes" ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true + } + ], "responses": { "200": { "description": "A sorted array of unique tag strings.", @@ -20285,9 +42212,71 @@ } } } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{username}/{slug}/statistics/download": { @@ -20326,8 +42315,40 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [] } }, "/resumes/{id}/statistics": { @@ -20406,8 +42427,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{id}/statistics/daily": { @@ -20474,8 +42537,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{id}/updates": { @@ -20501,19 +42606,147 @@ "200": { "description": "A stream of resume update invalidation events.", "content": { - "application/json": { + "text/event-stream": { "schema": { - "anyOf": [ - {}, + "oneOf": [ { - "not": {} + "type": "object", + "properties": { + "event": { + "const": "message" + }, + "data": { + "type": "object", + "properties": { + "type": { + "const": "resume.updated" + }, + "resumeId": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "mutation": { + "enum": [ + "sync", + "create", + "update", + "patch", + "lock", + "password", + "delete" + ], + "type": "string" + } + }, + "required": [ + "type", + "resumeId", + "userId", + "updatedAt", + "mutation" + ] + }, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event", + "data" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "done" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "error" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] } ] } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes": { @@ -20525,6 +42758,30 @@ "Resumes" ], "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true + }, { "name": "tags", "in": "query", @@ -20631,9 +42888,71 @@ } } } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "post": { "operationId": "createResume", @@ -20754,8 +43073,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{id}": { @@ -25086,8 +47447,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "put": { "operationId": "updateResume", @@ -29568,8 +51971,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "patch": { "operationId": "patchResume", @@ -34197,8 +56642,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteResume", @@ -34220,21 +56707,7 @@ ], "responses": { "200": { - "description": "The resume is in Trash.", - "content": { - "application/json": { - "schema": { - "anyOf": [ - { - "not": {} - }, - { - "not": {} - } - ] - } - } - } + "description": "The resume is in Trash." }, "403": { "description": "403", @@ -34295,8 +56768,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{username}/{slug}": { @@ -38602,8 +61117,40 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [] } }, "/resumes/{resumeId}/slug-check": { @@ -38670,8 +61217,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/import": { @@ -38771,8 +61360,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{id}/lock": { @@ -38815,23 +61446,136 @@ }, "responses": { "200": { - "description": "The resume lock status was updated successfully.", + "description": "The resume lock status was updated successfully." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "put": { + "operationId": "putResumeLock", + "summary": "Set resume lock status", + "description": "Toggles the locked status of a resume. When locked, a resume cannot be updated, patched, or deleted. Useful for protecting finalized resumes from accidental edits. Requires authentication.", + "tags": [ + "Resumes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The ID of the resume." + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "isLocked": { + "type": "boolean", + "description": "Whether the resume is locked." + } + }, + "required": [ + "isLocked" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The resume lock status was updated successfully." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{id}/password": { @@ -38875,23 +61619,51 @@ }, "responses": { "200": { - "description": "The resume password was set successfully.", + "description": "The resume password was set successfully." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "removeResumePassword", @@ -38913,23 +61685,51 @@ ], "responses": { "200": { - "description": "The resume password was removed successfully.", + "description": "The resume password was removed successfully." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{username}/{slug}/password/verify": { @@ -38972,6 +61772,7 @@ "password": { "type": "string", "minLength": 1, + "maxLength": 64, "description": "The password to verify." } }, @@ -38992,8 +61793,40 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [] } }, "/resumes/{id}/duplicate": { @@ -39055,8 +61888,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{resumeId}/versions": { @@ -39076,6 +61951,30 @@ "type": "string", "description": "The ID of the resume whose version history to list." } + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Maximum results (1–100). Omit both pagination fields for the legacy complete list." + }, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991, + "description": "Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given." + }, + "allowEmptyValue": true, + "allowReserved": true } ], "responses": { @@ -39133,9 +62032,71 @@ } } } + }, + "headers": { + "X-Total-Count": { + "description": "Total matching results before pagination.", + "schema": { + "type": "integer" + } + }, + "X-Limit": { + "description": "Page size, when pagination is requested.", + "schema": { + "type": "integer" + } + }, + "X-Offset": { + "description": "Zero-based offset, when pagination is requested.", + "schema": { + "type": "integer" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "post": { "operationId": "createResumeVersion", @@ -39229,8 +62190,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{resumeId}/versions/{versionId}": { @@ -43539,8 +66542,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "patch": { "operationId": "renameResumeVersion", @@ -43643,8 +66688,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteResumeVersion", @@ -43675,23 +66762,51 @@ ], "responses": { "200": { - "description": "The version was deleted.", + "description": "The version was deleted." + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", "content": { "application/json": { "schema": { - "anyOf": [ - { - "not": {} + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" }, - { - "not": {} - } - ] + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } } } } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{resumeId}/versions/{versionId}/restore": { @@ -48019,8 +71134,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/resumes/{id}/pdf": { @@ -48071,8 +71228,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/statistics": { @@ -48082,6 +71281,7 @@ "tags": [ "Platform Statistics" ], + "parameters": [], "responses": { "200": { "description": "OK", @@ -48116,8 +71316,40 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [] } }, "/statistics/github/stars": { @@ -48128,6 +71360,7 @@ "tags": [ "Platform Statistics" ], + "parameters": [], "responses": { "200": { "description": "The number of GitHub stars for the Reactive Resume repository.", @@ -48139,8 +71372,40 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [] } }, "/integrations/web-access": { @@ -48150,6 +71415,7 @@ "tags": [ "Integrations" ], + "parameters": [], "responses": { "200": { "description": "OK", @@ -48204,8 +71470,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "put": { "operationId": "saveWebAccessKey", @@ -48244,21 +71552,7 @@ }, "responses": { "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "anyOf": [ - { - "not": {} - }, - { - "not": {} - } - ] - } - } - } + "description": "OK" }, "403": { "description": "403", @@ -48379,8 +71673,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] }, "delete": { "operationId": "deleteWebAccessKey", @@ -48388,23 +71724,19 @@ "tags": [ "Integrations" ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "anyOf": [ - { - "not": {} - }, - { - "not": {} - } - ] - } + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object" } } + } + }, + "responses": { + "200": { + "description": "OK" }, "403": { "description": "403", @@ -48525,8 +71857,50 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } }, "/integrations/web-access/test": { @@ -48536,6 +71910,16 @@ "tags": [ "Integrations" ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, "responses": { "200": { "description": "OK", @@ -48700,8 +72084,28677 @@ } } } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } } - } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/{id}/exports/{format}": { + "get": { + "operationId": "exportResumeFile", + "summary": "Download a resume in PDF, DOCX, Markdown or JSON", + "tags": [ + "Resumes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + }, + { + "name": "format", + "in": "path", + "required": true, + "schema": { + "enum": [ + "pdf", + "docx", + "md", + "json" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "content-disposition": { + "schema": { + "type": "string" + }, + "required": true + } + }, + "content": { + "*/*": { + "schema": { + "type": "string", + "contentMediaType": "*/*" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/cover-letters/{id}/exports/{format}": { + "get": { + "operationId": "exportCoverLetterFile", + "summary": "Download a cover letter in PDF, DOCX, Markdown or JSON", + "description": "Uses live linked sender/design details. Structured letter greetings default to English; supply words to localize them. Dates use the document locale.", + "tags": [ + "Cover Letters" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + }, + { + "name": "format", + "in": "path", + "required": true, + "schema": { + "enum": [ + "pdf", + "docx", + "md", + "json" + ], + "type": "string" + } + }, + { + "name": "words", + "in": "query", + "schema": { + "type": "object", + "properties": { + "greeting": { + "type": "string", + "maxLength": 500, + "description": "Use {name} for the recipient." + }, + "teamGreeting": { + "type": "string", + "maxLength": 500 + }, + "hiringTeam": { + "type": "string", + "maxLength": 500 + }, + "signOff": { + "type": "string", + "maxLength": 500 + } + }, + "required": [ + "greeting", + "teamGreeting", + "hiringTeam", + "signOff" + ] + }, + "style": "deepObject", + "explode": true, + "allowEmptyValue": true, + "allowReserved": true + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "content-disposition": { + "schema": { + "type": "string" + }, + "required": true + } + }, + "content": { + "*/*": { + "schema": { + "type": "string", + "contentMediaType": "*/*" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/{id}/checks": { + "get": { + "operationId": "getResumeChecks", + "summary": "Check resume content for ATS issues", + "description": "Runs the editor's deterministic contact, date, layout, heading and writing checks. Respects ignored findings saved on the resume.", + "tags": [ + "Resumes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "findings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "enum": [ + "MISSING_NAME", + "MISSING_EMAIL", + "MALFORMED_EMAIL", + "MISSING_PHONE", + "MISSING_LOCATION", + "MALFORMED_URL", + "PICTURE_PRESENT", + "EMPTY_PERIOD", + "UNPARSEABLE_PERIOD", + "UNPARSEABLE_DATE", + "REVERSED_PERIOD", + "FUTURE_DATED_PERIOD", + "SECTION_MISSING_FROM_LAYOUT", + "NO_VISIBLE_EXPERIENCE", + "MISSING_EXPERIENCE_DESCRIPTION", + "NON_STANDARD_SECTION_TITLE", + "MULTI_COLUMN_PROSE_SECTION", + "PROSE_SECTION_IN_SIDEBAR", + "TWO_COLUMN_LAYOUT", + "SMALL_BODY_FONT", + "TIGHT_LINE_HEIGHT", + "TIGHT_PAGE_MARGINS" + ], + "type": "string" + }, + "severity": { + "enum": [ + "error", + "warning", + "info" + ], + "type": "string" + }, + "pointer": { + "type": "string" + }, + "key": { + "type": "string" + }, + "params": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + } + }, + "required": [ + "code", + "severity", + "pointer", + "key" + ] + }, + "readOnly": true + }, + "ignored": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "enum": [ + "MISSING_NAME", + "MISSING_EMAIL", + "MALFORMED_EMAIL", + "MISSING_PHONE", + "MISSING_LOCATION", + "MALFORMED_URL", + "PICTURE_PRESENT", + "EMPTY_PERIOD", + "UNPARSEABLE_PERIOD", + "UNPARSEABLE_DATE", + "REVERSED_PERIOD", + "FUTURE_DATED_PERIOD", + "SECTION_MISSING_FROM_LAYOUT", + "NO_VISIBLE_EXPERIENCE", + "MISSING_EXPERIENCE_DESCRIPTION", + "NON_STANDARD_SECTION_TITLE", + "MULTI_COLUMN_PROSE_SECTION", + "PROSE_SECTION_IN_SIDEBAR", + "TWO_COLUMN_LAYOUT", + "SMALL_BODY_FONT", + "TIGHT_LINE_HEIGHT", + "TIGHT_PAGE_MARGINS" + ], + "type": "string" + }, + "severity": { + "enum": [ + "error", + "warning", + "info" + ], + "type": "string" + }, + "pointer": { + "type": "string" + }, + "key": { + "type": "string" + }, + "params": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + } + }, + "required": [ + "code", + "severity", + "pointer", + "key" + ] + }, + "readOnly": true + }, + "counts": { + "type": "object", + "properties": { + "error": { + "type": "number" + }, + "warning": { + "type": "number" + }, + "info": { + "type": "number" + } + }, + "required": [ + "error", + "warning", + "info" + ] + }, + "totalRules": { + "type": "number" + }, + "passedRules": { + "type": "number" + }, + "score": { + "type": "number" + }, + "categories": { + "type": "object", + "propertyNames": { + "enum": [ + "contact", + "dates", + "layout", + "headings", + "writing" + ], + "type": "string" + }, + "additionalProperties": { + "type": "object", + "properties": { + "total": { + "type": "number" + }, + "passed": { + "type": "number" + } + }, + "required": [ + "total", + "passed" + ] + } + } + }, + "required": [ + "findings", + "ignored", + "counts", + "totalRules", + "passedRules", + "score", + "categories" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/pdf-checks": { + "post": { + "operationId": "createPdfCheck", + "summary": "Check a PDF for ATS readability", + "description": "Multipart PDF upload, up to 25 MB. Processes at most 30 pages, reports truncation and skipped checks, and stores nothing. Rate limited per client address. No authentication required.", + "tags": [ + "Checks" + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "contentMediaType": "*/*" + }, + "jobDescription": { + "type": "string", + "maxLength": 20000 + } + }, + "required": [ + "file" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "version": { + "const": 1 + }, + "score": { + "type": "number" + }, + "cappedBy": { + "type": "array", + "items": { + "enum": [ + "NO_TEXT_LAYER", + "IMAGE_ONLY_DOCUMENT", + "GARBLED_TEXT", + "TYPE3_FONT", + "INVALID_EMBEDDED_FONT", + "FILE_TOO_LARGE", + "ENCRYPTED_PDF", + "XFA_FORM", + "PDF_PORTFOLIO", + "REPLACEMENT_CHARACTERS", + "PRIVATE_USE_CHARACTERS", + "LIGATURE_CHARACTERS", + "NON_EMBEDDED_FONTS", + "INVISIBLE_TEXT", + "WHITE_TEXT", + "LOW_TEXT_DENSITY", + "HIGH_IMAGE_COVERAGE", + "LOST_WORD_SPACING", + "SPLIT_CHARACTER_SPACING", + "VERTICAL_TEXT", + "ROTATED_PAGES", + "ACROFORM_FIELDS", + "NON_STANDARD_PAGE_SIZE", + "UNTAGGED_PDF", + "MISSING_DOCUMENT_LANGUAGE", + "LARGE_FILE_SIZE", + "TRUNCATED_ANALYSIS", + "MULTI_COLUMN_LAYOUT", + "READING_ORDER_INVERSION", + "READING_ORDER_RISK", + "COLUMN_GUTTER", + "TABLE_LIKE_LAYOUT", + "TEXT_IN_MARGIN_ZONE", + "TIGHT_LINE_SPACING", + "SMALL_BODY_TEXT", + "MANY_DISTINCT_FONTS", + "NARROW_PAGE_MARGINS", + "TEXT_OUTSIDE_PAGE", + "NON_STANDARD_BULLET_GLYPHS", + "REPEATED_HEADER_FOOTER", + "DENSE_PAGE", + "NO_EXPERIENCE_SECTION", + "NO_RECOGNIZED_HEADINGS", + "FEW_SECTION_HEADINGS", + "NO_EDUCATION_SECTION", + "NO_SKILLS_SECTION", + "HEADINGS_NOT_DISTINGUISHED", + "VERY_SHORT_DOCUMENT", + "NO_BULLET_POINTS", + "NO_ROLE_LINES", + "NO_SUMMARY_SECTION", + "NO_EMAIL", + "NO_PHONE", + "NO_NAME_LINE", + "CONTACT_NOT_ON_FIRST_PAGE", + "EMAIL_SPLIT_ACROSS_ITEMS", + "LINK_TEXT_URL_MISMATCH", + "MULTIPLE_EMAILS", + "NO_PROFESSIONAL_LINK", + "BARE_URL_WITHOUT_PROTOCOL", + "PHONE_FORMAT_UNUSUAL", + "NO_DATES_FOUND", + "FEW_DATES", + "UNPARSEABLE_DATE_RANGE", + "REVERSED_DATE_RANGE", + "FUTURE_DATED_ENTRY", + "MIXED_DATE_FORMATS", + "NO_CURRENT_ROLE_MARKER", + "AMBIGUOUS_NUMERIC_DATE", + "NO_QUANTIFIED_IMPACT", + "WEAK_ACTION_VERBS", + "FIRST_PERSON_PRONOUNS", + "LONG_BULLETS", + "HIGH_PAGE_COUNT", + "EMPLOYMENT_GAP", + "ALL_CAPS_RUNS", + "REPEATED_PHRASES" + ], + "type": "string" + }, + "readOnly": true + }, + "categories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "enum": [ + "parseability", + "layout", + "sections", + "contact", + "dates" + ], + "type": "string" + }, + "score": { + "type": "number" + }, + "weight": { + "type": "number" + }, + "applicableChecks": { + "type": "number" + }, + "passedChecks": { + "type": "number" + }, + "skippedChecks": { + "type": "number" + } + }, + "required": [ + "category", + "score", + "weight", + "applicableChecks", + "passedChecks", + "skippedChecks" + ] + }, + "readOnly": true + }, + "checks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "enum": [ + "NO_TEXT_LAYER", + "IMAGE_ONLY_DOCUMENT", + "GARBLED_TEXT", + "TYPE3_FONT", + "INVALID_EMBEDDED_FONT", + "FILE_TOO_LARGE", + "ENCRYPTED_PDF", + "XFA_FORM", + "PDF_PORTFOLIO", + "REPLACEMENT_CHARACTERS", + "PRIVATE_USE_CHARACTERS", + "LIGATURE_CHARACTERS", + "NON_EMBEDDED_FONTS", + "INVISIBLE_TEXT", + "WHITE_TEXT", + "LOW_TEXT_DENSITY", + "HIGH_IMAGE_COVERAGE", + "LOST_WORD_SPACING", + "SPLIT_CHARACTER_SPACING", + "VERTICAL_TEXT", + "ROTATED_PAGES", + "ACROFORM_FIELDS", + "NON_STANDARD_PAGE_SIZE", + "UNTAGGED_PDF", + "MISSING_DOCUMENT_LANGUAGE", + "LARGE_FILE_SIZE", + "TRUNCATED_ANALYSIS", + "MULTI_COLUMN_LAYOUT", + "READING_ORDER_INVERSION", + "READING_ORDER_RISK", + "COLUMN_GUTTER", + "TABLE_LIKE_LAYOUT", + "TEXT_IN_MARGIN_ZONE", + "TIGHT_LINE_SPACING", + "SMALL_BODY_TEXT", + "MANY_DISTINCT_FONTS", + "NARROW_PAGE_MARGINS", + "TEXT_OUTSIDE_PAGE", + "NON_STANDARD_BULLET_GLYPHS", + "REPEATED_HEADER_FOOTER", + "DENSE_PAGE", + "NO_EXPERIENCE_SECTION", + "NO_RECOGNIZED_HEADINGS", + "FEW_SECTION_HEADINGS", + "NO_EDUCATION_SECTION", + "NO_SKILLS_SECTION", + "HEADINGS_NOT_DISTINGUISHED", + "VERY_SHORT_DOCUMENT", + "NO_BULLET_POINTS", + "NO_ROLE_LINES", + "NO_SUMMARY_SECTION", + "NO_EMAIL", + "NO_PHONE", + "NO_NAME_LINE", + "CONTACT_NOT_ON_FIRST_PAGE", + "EMAIL_SPLIT_ACROSS_ITEMS", + "LINK_TEXT_URL_MISMATCH", + "MULTIPLE_EMAILS", + "NO_PROFESSIONAL_LINK", + "BARE_URL_WITHOUT_PROTOCOL", + "PHONE_FORMAT_UNUSUAL", + "NO_DATES_FOUND", + "FEW_DATES", + "UNPARSEABLE_DATE_RANGE", + "REVERSED_DATE_RANGE", + "FUTURE_DATED_ENTRY", + "MIXED_DATE_FORMATS", + "NO_CURRENT_ROLE_MARKER", + "AMBIGUOUS_NUMERIC_DATE", + "NO_QUANTIFIED_IMPACT", + "WEAK_ACTION_VERBS", + "FIRST_PERSON_PRONOUNS", + "LONG_BULLETS", + "HIGH_PAGE_COUNT", + "EMPLOYMENT_GAP", + "ALL_CAPS_RUNS", + "REPEATED_PHRASES" + ], + "type": "string" + }, + "category": { + "enum": [ + "parseability", + "layout", + "sections", + "contact", + "dates", + "content" + ], + "type": "string" + }, + "severity": { + "enum": [ + "blocker", + "warning", + "tip" + ], + "type": "string" + }, + "status": { + "enum": [ + "pass", + "skip", + "fail" + ], + "type": "string" + }, + "skipReason": { + "enum": [ + "no-text", + "no-operators", + "not-english", + "not-applicable", + "encrypted", + "insufficient-data" + ], + "type": "string" + }, + "findings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "enum": [ + "NO_TEXT_LAYER", + "IMAGE_ONLY_DOCUMENT", + "GARBLED_TEXT", + "TYPE3_FONT", + "INVALID_EMBEDDED_FONT", + "FILE_TOO_LARGE", + "ENCRYPTED_PDF", + "XFA_FORM", + "PDF_PORTFOLIO", + "REPLACEMENT_CHARACTERS", + "PRIVATE_USE_CHARACTERS", + "LIGATURE_CHARACTERS", + "NON_EMBEDDED_FONTS", + "INVISIBLE_TEXT", + "WHITE_TEXT", + "LOW_TEXT_DENSITY", + "HIGH_IMAGE_COVERAGE", + "LOST_WORD_SPACING", + "SPLIT_CHARACTER_SPACING", + "VERTICAL_TEXT", + "ROTATED_PAGES", + "ACROFORM_FIELDS", + "NON_STANDARD_PAGE_SIZE", + "UNTAGGED_PDF", + "MISSING_DOCUMENT_LANGUAGE", + "LARGE_FILE_SIZE", + "TRUNCATED_ANALYSIS", + "MULTI_COLUMN_LAYOUT", + "READING_ORDER_INVERSION", + "READING_ORDER_RISK", + "COLUMN_GUTTER", + "TABLE_LIKE_LAYOUT", + "TEXT_IN_MARGIN_ZONE", + "TIGHT_LINE_SPACING", + "SMALL_BODY_TEXT", + "MANY_DISTINCT_FONTS", + "NARROW_PAGE_MARGINS", + "TEXT_OUTSIDE_PAGE", + "NON_STANDARD_BULLET_GLYPHS", + "REPEATED_HEADER_FOOTER", + "DENSE_PAGE", + "NO_EXPERIENCE_SECTION", + "NO_RECOGNIZED_HEADINGS", + "FEW_SECTION_HEADINGS", + "NO_EDUCATION_SECTION", + "NO_SKILLS_SECTION", + "HEADINGS_NOT_DISTINGUISHED", + "VERY_SHORT_DOCUMENT", + "NO_BULLET_POINTS", + "NO_ROLE_LINES", + "NO_SUMMARY_SECTION", + "NO_EMAIL", + "NO_PHONE", + "NO_NAME_LINE", + "CONTACT_NOT_ON_FIRST_PAGE", + "EMAIL_SPLIT_ACROSS_ITEMS", + "LINK_TEXT_URL_MISMATCH", + "MULTIPLE_EMAILS", + "NO_PROFESSIONAL_LINK", + "BARE_URL_WITHOUT_PROTOCOL", + "PHONE_FORMAT_UNUSUAL", + "NO_DATES_FOUND", + "FEW_DATES", + "UNPARSEABLE_DATE_RANGE", + "REVERSED_DATE_RANGE", + "FUTURE_DATED_ENTRY", + "MIXED_DATE_FORMATS", + "NO_CURRENT_ROLE_MARKER", + "AMBIGUOUS_NUMERIC_DATE", + "NO_QUANTIFIED_IMPACT", + "WEAK_ACTION_VERBS", + "FIRST_PERSON_PRONOUNS", + "LONG_BULLETS", + "HIGH_PAGE_COUNT", + "EMPLOYMENT_GAP", + "ALL_CAPS_RUNS", + "REPEATED_PHRASES" + ], + "type": "string" + }, + "severity": { + "enum": [ + "blocker", + "warning", + "tip" + ], + "type": "string" + }, + "category": { + "enum": [ + "parseability", + "layout", + "sections", + "contact", + "dates", + "content" + ], + "type": "string" + }, + "params": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + }, + "evidence": { + "type": "object", + "properties": { + "snippet": { + "type": "string" + }, + "page": { + "type": "number" + }, + "box": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ] + } + } + } + }, + "required": [ + "code", + "severity", + "category" + ] + }, + "readOnly": true + } + }, + "required": [ + "code", + "category", + "severity", + "status", + "findings" + ] + }, + "readOnly": true + }, + "findings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "enum": [ + "NO_TEXT_LAYER", + "IMAGE_ONLY_DOCUMENT", + "GARBLED_TEXT", + "TYPE3_FONT", + "INVALID_EMBEDDED_FONT", + "FILE_TOO_LARGE", + "ENCRYPTED_PDF", + "XFA_FORM", + "PDF_PORTFOLIO", + "REPLACEMENT_CHARACTERS", + "PRIVATE_USE_CHARACTERS", + "LIGATURE_CHARACTERS", + "NON_EMBEDDED_FONTS", + "INVISIBLE_TEXT", + "WHITE_TEXT", + "LOW_TEXT_DENSITY", + "HIGH_IMAGE_COVERAGE", + "LOST_WORD_SPACING", + "SPLIT_CHARACTER_SPACING", + "VERTICAL_TEXT", + "ROTATED_PAGES", + "ACROFORM_FIELDS", + "NON_STANDARD_PAGE_SIZE", + "UNTAGGED_PDF", + "MISSING_DOCUMENT_LANGUAGE", + "LARGE_FILE_SIZE", + "TRUNCATED_ANALYSIS", + "MULTI_COLUMN_LAYOUT", + "READING_ORDER_INVERSION", + "READING_ORDER_RISK", + "COLUMN_GUTTER", + "TABLE_LIKE_LAYOUT", + "TEXT_IN_MARGIN_ZONE", + "TIGHT_LINE_SPACING", + "SMALL_BODY_TEXT", + "MANY_DISTINCT_FONTS", + "NARROW_PAGE_MARGINS", + "TEXT_OUTSIDE_PAGE", + "NON_STANDARD_BULLET_GLYPHS", + "REPEATED_HEADER_FOOTER", + "DENSE_PAGE", + "NO_EXPERIENCE_SECTION", + "NO_RECOGNIZED_HEADINGS", + "FEW_SECTION_HEADINGS", + "NO_EDUCATION_SECTION", + "NO_SKILLS_SECTION", + "HEADINGS_NOT_DISTINGUISHED", + "VERY_SHORT_DOCUMENT", + "NO_BULLET_POINTS", + "NO_ROLE_LINES", + "NO_SUMMARY_SECTION", + "NO_EMAIL", + "NO_PHONE", + "NO_NAME_LINE", + "CONTACT_NOT_ON_FIRST_PAGE", + "EMAIL_SPLIT_ACROSS_ITEMS", + "LINK_TEXT_URL_MISMATCH", + "MULTIPLE_EMAILS", + "NO_PROFESSIONAL_LINK", + "BARE_URL_WITHOUT_PROTOCOL", + "PHONE_FORMAT_UNUSUAL", + "NO_DATES_FOUND", + "FEW_DATES", + "UNPARSEABLE_DATE_RANGE", + "REVERSED_DATE_RANGE", + "FUTURE_DATED_ENTRY", + "MIXED_DATE_FORMATS", + "NO_CURRENT_ROLE_MARKER", + "AMBIGUOUS_NUMERIC_DATE", + "NO_QUANTIFIED_IMPACT", + "WEAK_ACTION_VERBS", + "FIRST_PERSON_PRONOUNS", + "LONG_BULLETS", + "HIGH_PAGE_COUNT", + "EMPLOYMENT_GAP", + "ALL_CAPS_RUNS", + "REPEATED_PHRASES" + ], + "type": "string" + }, + "severity": { + "enum": [ + "blocker", + "warning", + "tip" + ], + "type": "string" + }, + "category": { + "enum": [ + "parseability", + "layout", + "sections", + "contact", + "dates", + "content" + ], + "type": "string" + }, + "params": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + }, + "evidence": { + "type": "object", + "properties": { + "snippet": { + "type": "string" + }, + "page": { + "type": "number" + }, + "box": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ] + } + } + } + }, + "required": [ + "code", + "severity", + "category" + ] + }, + "readOnly": true + }, + "tips": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "enum": [ + "NO_TEXT_LAYER", + "IMAGE_ONLY_DOCUMENT", + "GARBLED_TEXT", + "TYPE3_FONT", + "INVALID_EMBEDDED_FONT", + "FILE_TOO_LARGE", + "ENCRYPTED_PDF", + "XFA_FORM", + "PDF_PORTFOLIO", + "REPLACEMENT_CHARACTERS", + "PRIVATE_USE_CHARACTERS", + "LIGATURE_CHARACTERS", + "NON_EMBEDDED_FONTS", + "INVISIBLE_TEXT", + "WHITE_TEXT", + "LOW_TEXT_DENSITY", + "HIGH_IMAGE_COVERAGE", + "LOST_WORD_SPACING", + "SPLIT_CHARACTER_SPACING", + "VERTICAL_TEXT", + "ROTATED_PAGES", + "ACROFORM_FIELDS", + "NON_STANDARD_PAGE_SIZE", + "UNTAGGED_PDF", + "MISSING_DOCUMENT_LANGUAGE", + "LARGE_FILE_SIZE", + "TRUNCATED_ANALYSIS", + "MULTI_COLUMN_LAYOUT", + "READING_ORDER_INVERSION", + "READING_ORDER_RISK", + "COLUMN_GUTTER", + "TABLE_LIKE_LAYOUT", + "TEXT_IN_MARGIN_ZONE", + "TIGHT_LINE_SPACING", + "SMALL_BODY_TEXT", + "MANY_DISTINCT_FONTS", + "NARROW_PAGE_MARGINS", + "TEXT_OUTSIDE_PAGE", + "NON_STANDARD_BULLET_GLYPHS", + "REPEATED_HEADER_FOOTER", + "DENSE_PAGE", + "NO_EXPERIENCE_SECTION", + "NO_RECOGNIZED_HEADINGS", + "FEW_SECTION_HEADINGS", + "NO_EDUCATION_SECTION", + "NO_SKILLS_SECTION", + "HEADINGS_NOT_DISTINGUISHED", + "VERY_SHORT_DOCUMENT", + "NO_BULLET_POINTS", + "NO_ROLE_LINES", + "NO_SUMMARY_SECTION", + "NO_EMAIL", + "NO_PHONE", + "NO_NAME_LINE", + "CONTACT_NOT_ON_FIRST_PAGE", + "EMAIL_SPLIT_ACROSS_ITEMS", + "LINK_TEXT_URL_MISMATCH", + "MULTIPLE_EMAILS", + "NO_PROFESSIONAL_LINK", + "BARE_URL_WITHOUT_PROTOCOL", + "PHONE_FORMAT_UNUSUAL", + "NO_DATES_FOUND", + "FEW_DATES", + "UNPARSEABLE_DATE_RANGE", + "REVERSED_DATE_RANGE", + "FUTURE_DATED_ENTRY", + "MIXED_DATE_FORMATS", + "NO_CURRENT_ROLE_MARKER", + "AMBIGUOUS_NUMERIC_DATE", + "NO_QUANTIFIED_IMPACT", + "WEAK_ACTION_VERBS", + "FIRST_PERSON_PRONOUNS", + "LONG_BULLETS", + "HIGH_PAGE_COUNT", + "EMPLOYMENT_GAP", + "ALL_CAPS_RUNS", + "REPEATED_PHRASES" + ], + "type": "string" + }, + "severity": { + "enum": [ + "blocker", + "warning", + "tip" + ], + "type": "string" + }, + "category": { + "enum": [ + "parseability", + "layout", + "sections", + "contact", + "dates", + "content" + ], + "type": "string" + }, + "params": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + }, + "evidence": { + "type": "object", + "properties": { + "snippet": { + "type": "string" + }, + "page": { + "type": "number" + }, + "box": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ] + } + } + } + }, + "required": [ + "code", + "severity", + "category" + ] + }, + "readOnly": true + }, + "counts": { + "type": "object", + "properties": { + "blocker": { + "type": "number" + }, + "warning": { + "type": "number" + }, + "tip": { + "type": "number" + } + }, + "required": [ + "blocker", + "warning", + "tip" + ] + }, + "applicableChecks": { + "type": "number" + }, + "passedChecks": { + "type": "number" + }, + "skippedChecks": { + "type": "number" + }, + "jd": { + "anyOf": [ + { + "type": "object", + "properties": { + "terms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "term": { + "type": "string" + }, + "jdCount": { + "type": "number" + }, + "resumeCount": { + "type": "number" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "term", + "jdCount", + "resumeCount", + "weight" + ] + }, + "readOnly": true + }, + "matchedTerms": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "missingTerms": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "totalTerms": { + "type": "number" + }, + "matchedCount": { + "type": "number" + }, + "weightedCoverage": { + "type": "number" + }, + "stuffedTerms": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "documentHasHiddenText": { + "type": "boolean" + } + }, + "required": [ + "terms", + "matchedTerms", + "missingTerms", + "totalTerms", + "matchedCount", + "weightedCoverage", + "stuffedTerms", + "documentHasHiddenText" + ] + }, + { + "type": "null" + } + ] + }, + "file": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "sizeBytes": { + "type": "number" + }, + "magicBytesOk": { + "type": "boolean" + } + }, + "required": [ + "name", + "sizeBytes", + "magicBytesOk" + ] + }, + "document": { + "type": "object", + "properties": { + "pageCount": { + "type": "number" + }, + "truncated": { + "type": "boolean" + }, + "wordCount": { + "type": "number" + }, + "operatorsAvailable": { + "type": "boolean" + } + }, + "required": [ + "pageCount", + "truncated", + "wordCount", + "operatorsAvailable" + ] + }, + "fullText": { + "type": "string", + "description": "Extracted text in reading order, for the optional AI review." + } + }, + "required": [ + "version", + "score", + "cappedBy", + "categories", + "checks", + "findings", + "tips", + "counts", + "applicableChecks", + "passedChecks", + "skippedChecks", + "jd", + "file", + "document", + "fullText" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [] + } + }, + "/resumes/{id}/job-matches": { + "post": { + "operationId": "createResumeJobMatch", + "summary": "Match resume terms to a job posting", + "description": "Runs the editor's deterministic term matching and respects terms hidden on this resume. No AI provider is needed.", + "tags": [ + "Checks" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "jobDescription": { + "type": "string", + "minLength": 1, + "maxLength": 20000 + } + }, + "required": [ + "jobDescription" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "found": { + "type": "array", + "items": { + "type": "object", + "properties": { + "term": { + "type": "string" + }, + "jdCount": { + "type": "number" + }, + "resumeCount": { + "type": "number" + }, + "weight": { + "type": "number" + }, + "label": { + "type": "string" + } + }, + "required": [ + "term", + "jdCount", + "resumeCount", + "weight", + "label" + ] + } + }, + "missing": { + "type": "array", + "items": { + "type": "object", + "properties": { + "term": { + "type": "string" + }, + "jdCount": { + "type": "number" + }, + "resumeCount": { + "type": "number" + }, + "weight": { + "type": "number" + }, + "label": { + "type": "string" + } + }, + "required": [ + "term", + "jdCount", + "resumeCount", + "weight", + "label" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "found", + "missing", + "total" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/file-imports": { + "post": { + "operationId": "importResumeFile", + "summary": "Import a PDF, resume JSON or LinkedIn archive", + "description": "Multipart form with file and format fields. PDF imports read the text layer without AI. For AI-assisted PDF and Word imports, use the AI parsing endpoints then create a resume import.", + "tags": [ + "Resumes" + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "contentMediaType": "*/*" + }, + "format": { + "enum": [ + "REACTIVE_RESUME", + "REACTIVE_RESUME_V4", + "JSON_RESUME", + "LINKEDIN", + "PDF" + ], + "type": "string" + } + }, + "required": [ + "file", + "format" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "string", + "description": "The imported resume ID." + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/{id}/metadata": { + "patch": { + "operationId": "patchResumeMetadata", + "summary": "Update a resume", + "description": "Updates one or more fields of a resume identified by its ID. All fields are optional; only provided fields will be updated. Locked resumes cannot be updated. Requires authentication.", + "tags": [ + "Resumes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The tags of the resume." + }, + "data": { + "$ref": "#/components/schemas/ResumeData" + }, + "isPublic": { + "type": "boolean", + "description": "Whether the resume is public." + }, + "showDownloadButtons": { + "type": "boolean", + "description": "Whether download buttons are shown on the public resume page." + }, + "sessionId": { + "type": "string", + "maxLength": 64, + "description": "Identifies one editing session. Saves that share it keep one autosave version, refreshed at most every two minutes." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated resume with its full data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The tags of the resume." + }, + "isPublic": { + "type": "boolean", + "description": "Whether the resume is public." + }, + "showDownloadButtons": { + "type": "boolean", + "description": "Whether download buttons are shown on the public resume page." + }, + "isLocked": { + "type": "boolean", + "description": "Whether the resume is locked." + }, + "data": { + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the summary heading, icon, and decoration while retaining summary content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the summary is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the summary always begins on a new page." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "content" + ], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ] + }, + "description": "The items to display in the profiles section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the projects section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the projects of the author." + }, + "skills": { + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ] + }, + "description": "The items to display in the languages section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ] + }, + "description": "The items to display in the interests section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the awards section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the certifications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the publications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ] + }, + "description": "The items to display in the references section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "cover-letter", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": [ + "id", + "hidden", + "recipient", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "summary", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "profiles", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "experience", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "education", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "projects", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "enum": [ + "inline", + "list" + ], + "type": "string", + "default": "inline", + "description": "How skill keywords are displayed: inline separated by commas, or one bullet per keyword." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "skills", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the skill. Empty while the entry is a draft, which isn't printed." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "proficiency", + "level", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "languages", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "interests", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "awards", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "certifications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "publications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "volunteer", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "references", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + } + ] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "sidebarSide": { + "enum": [ + "left", + "right" + ], + "type": "string", + "description": "Which side of the page the sidebar column sits on in two-column templates. When unset, each template uses its own side (and right-to-left resumes mirror it)." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": [ + "fullWidth", + "main", + "sidebar" + ] + }, + "description": "The pages to display in the layout." + } + }, + "required": [ + "sidebarWidth", + "pages" + ], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "layout", + "page", + "design", + "typography", + "notes" + ], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + } + }, + "required": [ + "picture", + "basics", + "summary", + "sections", + "customSections", + "metadata" + ], + "additionalProperties": {} + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was created." + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was last updated." + }, + "hasPassword": { + "type": "boolean" + } + }, + "required": [ + "id", + "name", + "slug", + "tags", + "isPublic", + "showDownloadButtons", + "isLocked", + "data", + "createdAt", + "updatedAt", + "hasPassword" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "RESUME_SLUG_ALREADY_EXISTS" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "A resume with this slug already exists." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "RESUME_LOCKED" + }, + "status": { + "const": 403 + }, + "message": { + "type": "string", + "default": "Unlock the resume first." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/{id}/copies": { + "post": { + "operationId": "createResumeCopy", + "summary": "Duplicate a resume", + "description": "Creates a copy of an existing resume with the same data, and the given name and tags. The slug is optional: when omitted it is generated from the name and made unique across the user's resumes. Returns the ID of the duplicated resume. Requires authentication.", + "tags": [ + "Resumes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The ID of the resume." + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the resume." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The tags of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "description": "The slug of the copy. Generated from the name, and made unique, when omitted." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The ID of the duplicated resume.", + "content": { + "application/json": { + "schema": { + "type": "string", + "description": "The ID of the duplicated resume." + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/imports": { + "post": { + "operationId": "createResumeImport", + "summary": "Import a resume", + "description": "Creates a new resume from an existing ResumeData object (e.g. from a previously exported JSON file). A random name and slug are generated automatically. Returns the ID of the imported resume. Requires authentication.", + "tags": [ + "Resumes" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ResumeData" + } + }, + "required": [ + "data" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The ID of the imported resume.", + "content": { + "application/json": { + "schema": { + "type": "string", + "description": "The ID of the imported resume." + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "RESUME_SLUG_ALREADY_EXISTS" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "A resume with this slug already exists." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/{resumeId}/versions/{versionId}/restorations": { + "post": { + "operationId": "createResumeRestoration", + "summary": "Restore a resume version", + "description": "Non-destructively restores a resume to a previous version snapshot by writing that snapshot's data back through the normal update path. Prior versions are preserved and the restore itself becomes a new snapshot. Only the resume owner can restore versions. Requires authentication.", + "tags": [ + "Resumes" + ], + "parameters": [ + { + "name": "resumeId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The ID of the resume to restore." + } + }, + { + "name": "versionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The ID of the version snapshot to restore." + } + } + ], + "responses": { + "200": { + "description": "The restored resume with its full data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The tags of the resume." + }, + "isPublic": { + "type": "boolean", + "description": "Whether the resume is public." + }, + "showDownloadButtons": { + "type": "boolean", + "description": "Whether download buttons are shown on the public resume page." + }, + "isLocked": { + "type": "boolean", + "description": "Whether the resume is locked." + }, + "data": { + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the summary heading, icon, and decoration while retaining summary content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the summary is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the summary always begins on a new page." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "content" + ], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ] + }, + "description": "The items to display in the profiles section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the projects section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the projects of the author." + }, + "skills": { + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ] + }, + "description": "The items to display in the languages section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ] + }, + "description": "The items to display in the interests section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the awards section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the certifications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the publications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ] + }, + "description": "The items to display in the references section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "cover-letter", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": [ + "id", + "hidden", + "recipient", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "summary", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "profiles", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "experience", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "education", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "projects", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "enum": [ + "inline", + "list" + ], + "type": "string", + "default": "inline", + "description": "How skill keywords are displayed: inline separated by commas, or one bullet per keyword." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "skills", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the skill. Empty while the entry is a draft, which isn't printed." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "proficiency", + "level", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "languages", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "interests", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "awards", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "certifications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "publications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "volunteer", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "references", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + } + ] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "sidebarSide": { + "enum": [ + "left", + "right" + ], + "type": "string", + "description": "Which side of the page the sidebar column sits on in two-column templates. When unset, each template uses its own side (and right-to-left resumes mirror it)." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": [ + "fullWidth", + "main", + "sidebar" + ] + }, + "description": "The pages to display in the layout." + } + }, + "required": [ + "sidebarWidth", + "pages" + ], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "layout", + "page", + "design", + "typography", + "notes" + ], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + } + }, + "required": [ + "picture", + "basics", + "summary", + "sections", + "customSections", + "metadata" + ], + "additionalProperties": {} + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was created." + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was last updated." + }, + "hasPassword": { + "type": "boolean" + } + }, + "required": [ + "id", + "name", + "slug", + "tags", + "isPublic", + "showDownloadButtons", + "isLocked", + "data", + "createdAt", + "updatedAt", + "hasPassword" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/{username}/{slug}/password-verifications": { + "post": { + "operationId": "createResumePasswordVerification", + "summary": "Verify resume password", + "description": "Verifies a password for a password-protected public resume. On success, the viewer is granted access to view the resume data for the duration of their session. No authentication required.", + "tags": [ + "Resume Sharing" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The username of the resume owner." + } + }, + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The slug of the resume." + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "password": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The password to verify." + } + }, + "required": [ + "password" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The password was verified successfully and access has been granted.", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [] + } + }, + "/resumes/{resumeId}/slug-availability": { + "get": { + "operationId": "getResumeSlugAvailability", + "summary": "Check a resume address", + "description": "Checks whether a slug can be a resume's public address: lowercase letters and numbers in groups joined by single dashes, and not used by another of the user's resumes. When it can't, suggests one that works. Requires authentication.", + "tags": [ + "Resume Sharing" + ], + "parameters": [ + { + "name": "resumeId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The ID of the resume the slug is for." + } + }, + { + "name": "slug", + "in": "query", + "required": true, + "schema": { + "type": "string", + "maxLength": 64, + "description": "The slug to check." + }, + "allowEmptyValue": true, + "allowReserved": true + } + ], + "responses": { + "200": { + "description": "Whether the slug is available, and a suggestion when it isn't.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "enum": [ + "available", + "current", + "invalid", + "taken" + ], + "type": "string", + "description": "available: free to use. current: this resume's slug already. invalid: not lowercase letters, numbers and single dashes. taken: another of the user's resumes uses it." + }, + "takenBy": { + "type": "string", + "description": "The name of the resume that uses the slug, when taken." + }, + "suggestion": { + "type": "string", + "description": "A slug that would work instead." + } + }, + "required": [ + "status" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/resumes/{username}/{slug}/statistics/downloads": { + "post": { + "operationId": "createResumeDownload", + "summary": "Record a public resume PDF download", + "description": "Records a visitor's explicit PDF download after the browser starts saving the file. Requires access to the public resume. For password-protected resumes, first call verifyResumePassword (POST /resumes/{username}/{slug}/password/verify) with the password, then send the returned HttpOnly resume_access_ cookie with this request. A missing or invalid access cookie returns NEED_PASSWORD (HTTP 401); the cookie expires after 10 minutes. Owner downloads are excluded. Rate limited per resume and visitor.", + "tags": [ + "Resume Statistics" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The download event was accepted.", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [] + } + }, + "/cover-letters/{id}/copies": { + "post": { + "operationId": "createCoverLetterCopy", + "summary": "Duplicate a cover letter", + "description": "Creates a copy of an existing saved cover letter, optionally with a new name.", + "tags": [ + "Cover Letters" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The duplicated cover letter.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "style": { + "type": "object", + "properties": { + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ] + }, + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ] + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "page", + "design", + "typography" + ] + }, + "sectionId": { + "type": "string", + "minLength": 1 + }, + "itemId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "basics", + "picture", + "metadata", + "sectionId", + "itemId" + ] + }, + "layout": { + "enum": [ + "structured", + "freeform" + ], + "type": "string", + "description": "structured: recipient name, company and date fields, a greeting from the name, the body and a sign-off. freeform: the recipient block and body as written (letters from before structured letters).", + "default": "freeform" + }, + "recipientName": { + "type": "string", + "maxLength": 200, + "default": "", + "description": "Who the letter is to: a person, or a team." + }, + "recipientCompany": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The letter's date, as YYYY-MM-DD." + }, + "id": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceApplicationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderLinked": { + "type": "boolean", + "description": "The sender's details come live from the source resume, instead of the copy in `style`." + }, + "designLinked": { + "type": "boolean", + "description": "The design comes live from the source resume, instead of the copy in `style`." + }, + "isLocked": { + "type": "boolean", + "description": "Locked letters can't be edited or moved to Trash until they're unlocked." + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "name", + "recipient", + "content", + "style", + "id", + "sourceResumeId", + "sourceApplicationId", + "senderLinked", + "designLinked", + "isLocked", + "revision", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/cover-letters/imports": { + "post": { + "operationId": "createCoverLetterImport", + "summary": "Import a cover letter", + "description": "Creates a saved cover letter from an exported Reactive Resume cover-letter document.", + "tags": [ + "Cover Letters" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "document": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "style": { + "type": "object", + "properties": { + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ] + }, + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ] + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "styleRules": { + "type": "array", + "items": {}, + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "page", + "design", + "typography", + "styleRules" + ] + }, + "sectionId": { + "type": "string", + "minLength": 1 + }, + "itemId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "basics", + "picture", + "metadata", + "sectionId", + "itemId" + ] + }, + "layout": { + "enum": [ + "structured", + "freeform" + ], + "type": "string", + "description": "structured: recipient name, company and date fields, a greeting from the name, the body and a sign-off. freeform: the recipient block and body as written (letters from before structured letters).", + "default": "freeform" + }, + "recipientName": { + "type": "string", + "maxLength": 200, + "default": "", + "description": "Who the letter is to: a person, or a team." + }, + "recipientCompany": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The letter's date, as YYYY-MM-DD." + }, + "format": { + "const": "reactive-resume-cover-letter" + }, + "version": { + "const": 1 + } + }, + "required": [ + "name", + "recipient", + "content", + "style", + "format", + "version" + ] + } + }, + "required": [ + "document" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The imported cover letter.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "style": { + "type": "object", + "properties": { + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ] + }, + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ] + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "page", + "design", + "typography" + ] + }, + "sectionId": { + "type": "string", + "minLength": 1 + }, + "itemId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "basics", + "picture", + "metadata", + "sectionId", + "itemId" + ] + }, + "layout": { + "enum": [ + "structured", + "freeform" + ], + "type": "string", + "description": "structured: recipient name, company and date fields, a greeting from the name, the body and a sign-off. freeform: the recipient block and body as written (letters from before structured letters).", + "default": "freeform" + }, + "recipientName": { + "type": "string", + "maxLength": 200, + "default": "", + "description": "Who the letter is to: a person, or a team." + }, + "recipientCompany": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The letter's date, as YYYY-MM-DD." + }, + "id": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceApplicationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderLinked": { + "type": "boolean", + "description": "The sender's details come live from the source resume, instead of the copy in `style`." + }, + "designLinked": { + "type": "boolean", + "description": "The design comes live from the source resume, instead of the copy in `style`." + }, + "isLocked": { + "type": "boolean", + "description": "Locked letters can't be edited or moved to Trash until they're unlocked." + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "name", + "recipient", + "content", + "style", + "id", + "sourceResumeId", + "sourceApplicationId", + "senderLinked", + "designLinked", + "isLocked", + "revision", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/cover-letters/{id}/style-refreshes": { + "post": { + "operationId": "createCoverLetterStyleRefresh", + "summary": "Refresh cover letter style", + "description": "Refreshes a cover letter's style from a selected resume while preserving its content and template.", + "tags": [ + "Cover Letters" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "expectedRevision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "resumeId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "expectedRevision", + "resumeId" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The cover letter with refreshed style.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "style": { + "type": "object", + "properties": { + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ] + }, + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ] + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "page", + "design", + "typography" + ] + }, + "sectionId": { + "type": "string", + "minLength": 1 + }, + "itemId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "basics", + "picture", + "metadata", + "sectionId", + "itemId" + ] + }, + "layout": { + "enum": [ + "structured", + "freeform" + ], + "type": "string", + "description": "structured: recipient name, company and date fields, a greeting from the name, the body and a sign-off. freeform: the recipient block and body as written (letters from before structured letters).", + "default": "freeform" + }, + "recipientName": { + "type": "string", + "maxLength": 200, + "default": "", + "description": "Who the letter is to: a person, or a team." + }, + "recipientCompany": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The letter's date, as YYYY-MM-DD." + }, + "id": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceApplicationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderLinked": { + "type": "boolean", + "description": "The sender's details come live from the source resume, instead of the copy in `style`." + }, + "designLinked": { + "type": "boolean", + "description": "The design comes live from the source resume, instead of the copy in `style`." + }, + "isLocked": { + "type": "boolean", + "description": "Locked letters can't be edited or moved to Trash until they're unlocked." + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "name", + "recipient", + "content", + "style", + "id", + "sourceResumeId", + "sourceApplicationId", + "senderLinked", + "designLinked", + "isLocked", + "revision", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/cover-letters/{id}/drafts": { + "post": { + "operationId": "createCoverLetterDraft", + "summary": "Draft a cover letter's body", + "description": "Streams a draft of the letter's body, as text, from its linked resume and its application's posting, using only facts from them. Nothing is saved. `shorter` and `personal` revise a previous draft.", + "tags": [ + "Cover Letters", + "AI" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "variant": { + "enum": [ + "draft", + "shorter", + "personal" + ], + "type": "string", + "default": "draft", + "description": "draft writes the body; shorter and personal revise `previous`." + }, + "previous": { + "type": "string", + "maxLength": 20000, + "description": "The draft being revised." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The draft, streamed as text.", + "content": { + "text/event-stream": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "event": { + "const": "message" + }, + "data": { + "type": "string" + }, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event", + "data" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "done" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "error" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/cover-letters/{id}/versions/{versionId}/restorations": { + "post": { + "operationId": "createCoverLetterRestoration", + "summary": "Restore a cover letter version", + "description": "Saves the current letter as \"Before restore\", then makes the letter read as the version did. Linked details and design keep coming from the resume.", + "tags": [ + "Cover Letters" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + }, + { + "name": "versionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1 + } + } + ], + "responses": { + "200": { + "description": "The restored letter.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "recipient": { + "type": "string", + "maxLength": 20000 + }, + "content": { + "type": "string", + "maxLength": 100000 + }, + "style": { + "type": "object", + "properties": { + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ] + }, + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ] + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "page", + "design", + "typography" + ] + }, + "sectionId": { + "type": "string", + "minLength": 1 + }, + "itemId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "basics", + "picture", + "metadata", + "sectionId", + "itemId" + ] + }, + "layout": { + "enum": [ + "structured", + "freeform" + ], + "type": "string", + "description": "structured: recipient name, company and date fields, a greeting from the name, the body and a sign-off. freeform: the recipient block and body as written (letters from before structured letters).", + "default": "freeform" + }, + "recipientName": { + "type": "string", + "maxLength": 200, + "default": "", + "description": "Who the letter is to: a person, or a team." + }, + "recipientCompany": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "letterDate": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The letter's date, as YYYY-MM-DD." + }, + "id": { + "type": "string" + }, + "sourceResumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceApplicationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderLinked": { + "type": "boolean", + "description": "The sender's details come live from the source resume, instead of the copy in `style`." + }, + "designLinked": { + "type": "boolean", + "description": "The design comes live from the source resume, instead of the copy in `style`." + }, + "isLocked": { + "type": "boolean", + "description": "Locked letters can't be edited or moved to Trash until they're unlocked." + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "name", + "recipient", + "content", + "style", + "id", + "sourceResumeId", + "sourceApplicationId", + "senderLinked", + "designLinked", + "isLocked", + "revision", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/documents/{type}/{id}/name": { + "patch": { + "operationId": "patchDocumentName", + "summary": "Rename a document", + "description": "Renames a resume or letter. Requires authentication.", + "tags": [ + "Documents" + ], + "parameters": [ + { + "name": "type", + "in": "path", + "required": true, + "schema": { + "enum": [ + "resume", + "letter" + ], + "type": "string", + "description": "resume, or letter (a saved cover letter)." + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The ID of the resume or cover letter." + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/documents/{type}/{id}/tags": { + "put": { + "operationId": "putDocumentTags", + "summary": "Set a document's tags", + "description": "Replaces a resume's or letter's tags. Requires authentication.", + "tags": [ + "Documents" + ], + "parameters": [ + { + "name": "type", + "in": "path", + "required": true, + "schema": { + "enum": [ + "resume", + "letter" + ], + "type": "string", + "description": "resume, or letter (a saved cover letter)." + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The ID of the resume or cover letter." + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tags": { + "type": "array", + "maxItems": 20, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 40 + } + } + }, + "required": [ + "tags" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/documents/{type}/{id}/lock": { + "put": { + "operationId": "putDocumentLock", + "summary": "Lock or unlock a document", + "description": "Locks a resume or letter against edits and Trash, or unlocks it. Requires authentication.", + "tags": [ + "Documents" + ], + "parameters": [ + { + "name": "type", + "in": "path", + "required": true, + "schema": { + "enum": [ + "resume", + "letter" + ], + "type": "string", + "description": "resume, or letter (a saved cover letter)." + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The ID of the resume or cover letter." + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "isLocked": { + "type": "boolean" + } + }, + "required": [ + "isLocked" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/documents/{type}/{id}/application": { + "put": { + "operationId": "putDocumentApplication", + "summary": "Link a document to an application", + "description": "Records the job application a resume or letter was made for, or clears it. Requires authentication.", + "tags": [ + "Documents" + ], + "parameters": [ + { + "name": "type", + "in": "path", + "required": true, + "schema": { + "enum": [ + "resume", + "letter" + ], + "type": "string", + "description": "resume, or letter (a saved cover letter)." + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The ID of the resume or cover letter." + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "applicationId": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ], + "description": "The application to link, or null to unlink." + } + }, + "required": [ + "applicationId" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/documents/{type}/{id}/trash": { + "put": { + "operationId": "putDocumentTrash", + "summary": "Move a document to Trash", + "description": "Moves a resume or letter to Trash, where it stays for 30 days. A resume in Trash isn't shared. Locked documents can't be moved. Requires authentication.", + "tags": [ + "Documents" + ], + "parameters": [ + { + "name": "type", + "in": "path", + "required": true, + "schema": { + "enum": [ + "resume", + "letter" + ], + "type": "string", + "description": "resume, or letter (a saved cover letter)." + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The ID of the resume or cover letter." + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "delete": { + "operationId": "deleteDocumentTrash", + "summary": "Restore a document", + "description": "Brings a document back from Trash. Requires authentication.", + "tags": [ + "Documents" + ], + "parameters": [ + { + "name": "type", + "in": "path", + "required": true, + "schema": { + "enum": [ + "resume", + "letter" + ], + "type": "string", + "description": "resume, or letter (a saved cover letter)." + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The ID of the resume or cover letter." + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/documents/{type}/{id}": { + "delete": { + "operationId": "deleteDocumentPermanently", + "summary": "Delete a document now", + "description": "Permanently deletes a document that is already in Trash. Requires authentication.", + "tags": [ + "Documents" + ], + "parameters": [ + { + "name": "type", + "in": "path", + "required": true, + "schema": { + "enum": [ + "resume", + "letter" + ], + "type": "string", + "description": "resume, or letter (a saved cover letter)." + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "minLength": 1, + "description": "The ID of the resume or cover letter." + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/documents/copies": { + "post": { + "operationId": "createDocumentCopy", + "summary": "Copy a resume for a job", + "description": "Duplicates a resume and links the copy to a job application. Saved jobs select the copy; submitted document history is preserved. Requires authentication.", + "tags": [ + "Documents" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "resumeId": { + "type": "string", + "minLength": 1, + "description": "The resume to copy." + }, + "applicationId": { + "type": "string", + "minLength": 1, + "description": "The job the copy is for; omit for no job yet." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "description": "Defaults to “{source} — {company}”." + } + }, + "required": [ + "resumeId" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "string", + "description": "The ID of the copy." + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/applications/ai/posting-searches": { + "post": { + "operationId": "createPostingSearch", + "summary": "Search job postings", + "description": "Searches using the selected Firecrawl, Tavily or Exa connection. Returns up to five public links to review and import. Requires authentication.", + "tags": [ + "Applications", + "AI" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "minLength": 2, + "maxLength": 500 + } + }, + "required": [ + "query" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "title": { + "type": "string", + "maxLength": 1000 + }, + "description": { + "type": "string", + "maxLength": 5000, + "default": "" + } + }, + "required": [ + "url", + "title" + ] + } + } + } + } + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "RATE_LIMIT_EXCEEDED" + }, + "status": { + "const": 429 + }, + "message": { + "type": "string", + "default": "Too many web requests. Try again later." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "SEARCH_FAILED" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "Job search couldn't be reached. Try again or paste a posting link." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "SEARCH_UNAVAILABLE" + }, + "status": { + "const": 503 + }, + "message": { + "type": "string", + "default": "Job search isn't configured on this server." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/applications/ai/posting-parses": { + "post": { + "operationId": "createPostingParse", + "summary": "Read a job posting", + "description": "Reads a job link or pasted posting text into role, company, location, salary and requirements, and returns the posting text to save with the application. Links must be public https pages. Without an AI provider, only a page's own job data (JSON-LD) fills the fields. Requires authentication.", + "tags": [ + "Applications", + "AI" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "input": { + "type": "string", + "minLength": 1, + "maxLength": 100000 + } + }, + "required": [ + "input" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "role": { + "type": "string" + }, + "company": { + "type": "string" + }, + "location": { + "type": "string" + }, + "salary": { + "type": "string" + }, + "requirements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What the posting asks for, one short item each." + }, + "jobDescription": { + "type": "string", + "description": "The posting's text, to save with the application." + }, + "sourceUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The link, when a link was given." + }, + "postingSource": { + "type": "object", + "properties": { + "method": { + "enum": [ + "paste", + "builtin", + "firecrawl", + "tavily", + "exa" + ], + "type": "string" + }, + "format": { + "enum": [ + "text", + "markdown" + ], + "type": "string" + }, + "requestedUrl": { + "type": "string", + "format": "uri" + }, + "resolvedUrl": { + "type": "string", + "format": "uri" + }, + "retrievedAt": { + "type": "string", + "format": "date-time" + }, + "providerFetchedAt": { + "type": "string", + "format": "date-time" + }, + "truncated": { + "type": "boolean" + }, + "completeness": { + "enum": [ + "unknown", + "incomplete" + ], + "type": "string" + }, + "fallbackReason": { + "enum": [ + "unsafe-url", + "unreachable", + "not-a-page", + "too-large", + "auth", + "quota", + "malformed", + "empty", + "challenge", + "timeout", + "unavailable", + "rate-limit" + ], + "type": "string" + } + }, + "required": [ + "method", + "format", + "truncated", + "completeness" + ] + }, + "enrichmentWarning": { + "anyOf": [ + { + "enum": [ + "ai-unavailable" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "filledBy": { + "enum": [ + "ai", + "page", + "none" + ], + "type": "string", + "description": "What filled the fields: the AI provider, the page's own job data, or nothing (fill them in)." + } + }, + "required": [ + "role", + "company", + "location", + "salary", + "requirements", + "jobDescription", + "sourceUrl", + "postingSource", + "enrichmentWarning", + "filledBy" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "Invalid application or AI request." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "POSTING_UNREADABLE" + }, + "status": { + "const": 422 + }, + "message": { + "type": "string", + "default": "That link couldn't be read. Paste the posting text instead." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "RATE_LIMIT_EXCEEDED" + }, + "status": { + "const": 429 + }, + "message": { + "type": "string", + "default": "Too many web requests. Try again later." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/applications/ai/autofills": { + "post": { + "operationId": "createApplicationAutofill", + "tags": [ + "Applications", + "AI" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "jobDescription": { + "type": "string", + "minLength": 1, + "maxLength": 20000 + } + }, + "required": [ + "jobDescription" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "company": { + "type": "string" + }, + "role": { + "type": "string" + }, + "location": { + "type": "string" + }, + "salary": { + "type": "string" + } + }, + "required": [ + "company", + "role", + "location", + "salary" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "Invalid application or AI request." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/applications/{id}/ai/match-scores": { + "post": { + "operationId": "createApplicationMatchScore", + "tags": [ + "Applications", + "AI" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "score": {}, + "gaps": {}, + "strengths": {} + } + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "Invalid application or AI request." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/applications/{id}/ai/message-drafts": { + "post": { + "operationId": "createApplicationMessageDraft", + "tags": [ + "Applications", + "AI" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "kind": { + "enum": [ + "cover-letter", + "follow-up" + ], + "type": "string" + } + }, + "required": [ + "kind" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "coverLetterId": { + "type": "string" + } + }, + "required": [ + "text" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "Invalid application or AI request." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/applications/{id}/ai/tailored-resumes": { + "post": { + "operationId": "createTailoredResume", + "tags": [ + "Applications", + "AI" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "resumeId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "resumeId", + "name" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "Invalid application or AI request." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/applications/imports": { + "post": { + "operationId": "createApplicationImport", + "summary": "Bulk import applications", + "description": "Creates many applications at once from a parsed CSV. Each item requires company and role. Returns the number imported. Requires authentication.", + "tags": [ + "Applications" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "minItems": 1, + "maxItems": 500, + "items": { + "type": "object", + "properties": { + "company": { + "type": "string", + "minLength": 1, + "description": "The company applied to." + }, + "role": { + "type": "string", + "minLength": 1, + "description": "The role / job title." + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "salary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sourceUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "jobDescription": { + "anyOf": [ + { + "type": "string", + "maxLength": 20000 + }, + { + "type": "null" + } + ] + }, + "postingSource": { + "anyOf": [ + { + "type": "object", + "properties": { + "method": { + "enum": [ + "paste", + "builtin", + "firecrawl", + "tavily", + "exa" + ], + "type": "string" + }, + "format": { + "enum": [ + "text", + "markdown" + ], + "type": "string" + }, + "requestedUrl": { + "type": "string", + "format": "uri" + }, + "resolvedUrl": { + "type": "string", + "format": "uri" + }, + "retrievedAt": { + "type": "string", + "format": "date-time" + }, + "providerFetchedAt": { + "type": "string", + "format": "date-time" + }, + "truncated": { + "type": "boolean" + }, + "completeness": { + "enum": [ + "unknown", + "incomplete" + ], + "type": "string" + }, + "fallbackReason": { + "enum": [ + "unsafe-url", + "unreachable", + "not-a-page", + "too-large", + "auth", + "quota", + "malformed", + "empty", + "challenge", + "timeout", + "unavailable", + "rate-limit" + ], + "type": "string" + } + }, + "required": [ + "method", + "format", + "truncated", + "completeness" + ] + }, + { + "type": "null" + } + ] + }, + "notes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resumeFileName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "coverLetterName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "followUpAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "followUpNote": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "default": "" + }, + "type": { + "type": "string", + "default": "" + }, + "email": { + "type": "string", + "default": "" + }, + "phone": { + "type": "string", + "default": "" + } + }, + "required": [ + "name" + ] + } + }, + "resumeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked Reactive Resume, if any." + }, + "coverLetterId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The linked saved cover letter, if any." + }, + "requirements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "What the posting asks for, as read when the application was added." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "enum": [ + "saved", + "applied", + "screening", + "interview", + "offer", + "closed" + ], + "type": "string" + }, + "closedReason": { + "anyOf": [ + { + "enum": [ + "not-selected", + "withdrew", + "accepted-other", + "no-response" + ], + "type": "string" + }, + { + "type": "null" + } + ] + }, + "stageEnteredAt": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + } + }, + "required": [ + "company", + "role" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The number of applications imported.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "imported": { + "type": "number" + } + }, + "required": [ + "imported" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/ai-providers/{id}/tests": { + "post": { + "operationId": "createAiProviderTest", + "summary": "Test saved AI provider", + "description": "Decrypts the saved API key server-side and validates the provider/model connection.", + "tags": [ + "AI Providers" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "managed": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "provider": { + "enum": [ + "openai", + "anthropic", + "gemini", + "vercel-ai-gateway", + "openrouter", + "mistral", + "cohere", + "xai", + "groq", + "deepseek", + "togetherai", + "fireworks", + "cerebras", + "perplexity", + "ollama", + "openai-compatible" + ], + "type": "string" + }, + "model": { + "type": "string" + }, + "baseURL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "enabled": { + "type": "boolean" + }, + "testStatus": { + "type": "string" + }, + "testError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "apiKeyPreview": { + "type": "string" + }, + "apiKeyFingerprint": { + "type": "string" + }, + "lastTestedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "lastUsedAt": { + "anyOf": [ + { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date" + } + }, + "required": [ + "managed", + "id", + "label", + "provider", + "model", + "baseURL", + "enabled", + "testStatus", + "testError", + "apiKeyPreview", + "apiKeyFingerprint", + "lastTestedAt", + "lastUsedAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "Invalid AI provider configuration." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "FORBIDDEN" + }, + "status": { + "const": 403 + }, + "message": { + "type": "string", + "default": "AI is managed by the server." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "NOT_FOUND" + }, + "status": { + "const": 404 + }, + "message": { + "type": "string", + "default": "AI provider was not found." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "412": { + "description": "412", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "PRECONDITION_FAILED" + }, + "status": { + "const": 412 + }, + "message": { + "type": "string", + "default": "AI agent workspace is not configured." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/integrations/web-access/tests": { + "post": { + "operationId": "createWebAccessTest", + "summary": "Test provider search and reading independently", + "tags": [ + "Integrations" + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "search": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + }, + "read": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + } + }, + "required": [ + "search", + "read" + ] + } + } + } + }, + "412": { + "description": "412", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "PRECONDITION_FAILED" + }, + "status": { + "const": 412 + }, + "message": { + "type": "string", + "default": "No external web connection is configured." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "RATE_LIMIT_EXCEEDED" + }, + "status": { + "const": 429 + }, + "message": { + "type": "string", + "default": "Too many web requests. Try again later." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/agent/threads/{threadId}/messages": { + "post": { + "operationId": "createAgentMessage", + "summary": "Send agent message", + "tags": [ + "Agent" + ], + "parameters": [ + { + "name": "threadId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "role": { + "enum": [ + "system", + "user", + "assistant" + ], + "type": "string" + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": {} + } + }, + "metadata": {} + }, + "required": [ + "id", + "role", + "parts" + ] + }, + "attachmentIds": { + "type": "array", + "maxItems": 10, + "items": { + "type": "string", + "minLength": 1 + } + }, + "context": { + "type": "object", + "properties": { + "document": { + "type": "boolean", + "description": "Share the open document (on by default)." + }, + "posting": { + "type": "boolean", + "description": "Share the job posting it's for (on by default)." + }, + "applicationId": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } + } + } + }, + "required": [ + "message" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/event-stream": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "event": { + "const": "message" + }, + "data": { + "type": "string" + }, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event", + "data" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "done" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "error" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/agent/threads/{threadId}/run": { + "delete": { + "operationId": "deleteAgentRun", + "summary": "Stop active agent run", + "tags": [ + "Agent" + ], + "parameters": [ + { + "name": "threadId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "partialMessage": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "role": { + "enum": [ + "system", + "user", + "assistant" + ], + "type": "string" + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": {} + } + }, + "metadata": {} + }, + "required": [ + "id", + "role", + "parts" + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK" + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/agent/threads/{threadId}/stream": { + "get": { + "operationId": "getAgentStream", + "summary": "Resume agent message stream", + "tags": [ + "Agent" + ], + "parameters": [ + { + "name": "threadId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/event-stream": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "event": { + "const": "message" + }, + "data": { + "type": "string" + }, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event", + "data" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "done" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "const": "error" + }, + "data": {}, + "id": { + "type": "string" + }, + "retry": { + "type": "number" + } + }, + "required": [ + "event" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/agent/threads/{threadId}/messages/{messageId}/edits": { + "patch": { + "operationId": "patchAgentMessageEdits", + "summary": "Record what happened to proposed edits", + "description": "Records that proposed edits were accepted, rejected, or are pending again (after an undo), so past conversations show their outcome.", + "tags": [ + "Agent" + ], + "parameters": [ + { + "name": "threadId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "messageId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "toolCallId": { + "type": "string" + }, + "edits": { + "type": "array", + "minItems": 1, + "maxItems": 50, + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "status": { + "enum": [ + "pending", + "accepted", + "rejected" + ], + "type": "string" + } + }, + "required": [ + "id", + "status" + ] + } + } + }, + "required": [ + "toolCallId", + "edits" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "editsProposed": { + "type": "number" + }, + "editsAccepted": { + "type": "number" + } + }, + "required": [ + "editsProposed", + "editsAccepted" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/ai/pdf-parses": { + "post": { + "operationId": "createPdfParse", + "summary": "Parse a PDF file into resume data", + "description": "Extracts structured resume data from a PDF file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials. Returns a complete ResumeData object. Requires authentication.", + "tags": [ + "AI" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "aiProviderId": { + "type": "string" + }, + "file": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "data": { + "type": "string", + "maxLength": 13981018 + } + }, + "required": [ + "name", + "data" + ] + } + }, + "required": [ + "file" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The PDF was successfully parsed into structured resume data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the summary heading, icon, and decoration while retaining summary content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the summary is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the summary always begins on a new page." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "content" + ], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ] + }, + "description": "The items to display in the profiles section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the projects section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the projects of the author." + }, + "skills": { + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ] + }, + "description": "The items to display in the languages section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ] + }, + "description": "The items to display in the interests section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the awards section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the certifications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the publications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ] + }, + "description": "The items to display in the references section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "cover-letter", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": [ + "id", + "hidden", + "recipient", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "summary", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "profiles", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "experience", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "education", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "projects", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "enum": [ + "inline", + "list" + ], + "type": "string", + "default": "inline", + "description": "How skill keywords are displayed: inline separated by commas, or one bullet per keyword." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "skills", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the skill. Empty while the entry is a draft, which isn't printed." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "proficiency", + "level", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "languages", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "interests", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "awards", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "certifications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "publications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "volunteer", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "references", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + } + ] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "sidebarSide": { + "enum": [ + "left", + "right" + ], + "type": "string", + "description": "Which side of the page the sidebar column sits on in two-column templates. When unset, each template uses its own side (and right-to-left resumes mirror it)." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": [ + "fullWidth", + "main", + "sidebar" + ] + }, + "description": "The pages to display in the layout." + } + }, + "required": [ + "sidebarWidth", + "pages" + ], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "layout", + "page", + "design", + "typography", + "notes" + ], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + } + }, + "required": [ + "picture", + "basics", + "summary", + "sections", + "customSections", + "metadata" + ], + "additionalProperties": {} + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "The AI returned an improperly formatted structure." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/ai/docx-parses": { + "post": { + "operationId": "createDocxParse", + "summary": "Parse a DOCX file into resume data", + "description": "Extracts structured resume data from a DOCX or DOC file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials and the document's media type. Returns a complete ResumeData object. Requires authentication.", + "tags": [ + "AI" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "aiProviderId": { + "type": "string" + }, + "file": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "data": { + "type": "string", + "maxLength": 13981018 + } + }, + "required": [ + "name", + "data" + ] + }, + "mediaType": { + "enum": [ + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ], + "type": "string" + } + }, + "required": [ + "file", + "mediaType" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The DOCX was successfully parsed into structured resume data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "fit": { + "enum": [ + "cover", + "contain" + ], + "type": "string", + "description": "How the picture fits its frame: cover crops overflow, while contain preserves the whole image." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "fit", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": [ + "url", + "label" + ], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": [ + "id", + "icon", + "text", + "link" + ] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": [ + "name", + "headline", + "email", + "phone", + "location", + "website", + "customFields" + ], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the summary heading, icon, and decoration while retaining summary content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the summary is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the summary always begins on a new page." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "content" + ], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ] + }, + "description": "The items to display in the profiles section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the projects section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the projects of the author." + }, + "skills": { + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ] + }, + "description": "The items to display in the languages section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ] + }, + "description": "The items to display in the interests section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the awards section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the certifications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ] + }, + "description": "The items to display in the publications section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ] + }, + "description": "The items to display in the references section." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "items" + ], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "cover-letter", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": [ + "id", + "hidden", + "recipient", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "summary", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "content" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "profiles", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "network": { + "type": "string", + "description": "The name of the network or platform. Empty while the entry is a draft, which isn't printed." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "network", + "username", + "website" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "experience", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "description": "The name of the company or organization. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The overall period of time at the company, as text. When multiple roles are used, this should reflect the total tenure. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the role. Usually generated as a UUID." + }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "description": { + "type": "string", + "description": "The description of this specific role. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "position", + "period", + "description" + ] + }, + "description": "List of individual roles held at this company to show career progression." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description", + "roles" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "education", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "description": "The name of the school or institution. Empty while the entry is a draft, which isn't printed." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "projects", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the project. Empty while the entry is a draft, which isn't printed." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "enum": [ + "inline", + "list" + ], + "type": "string", + "default": "inline", + "description": "How skill keywords are displayed: inline separated by commas, or one bullet per keyword." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "skills", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the skill. Empty while the entry is a draft, which isn't printed." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "proficiency", + "level", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "languages", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "description": "The name of the language the author knows. Empty while the entry is a draft, which isn't printed." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": [ + "id", + "hidden", + "language", + "fluency", + "level" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "interests", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "iconColor": { + "type": "string", + "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color." + }, + "name": { + "type": "string", + "description": "The name of the interest/hobby. Empty while the entry is a draft, which isn't printed." + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": [ + "id", + "hidden", + "icon", + "iconColor", + "name", + "keywords" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "awards", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the award. Empty while the entry is a draft, which isn't printed." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "awarder", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "certifications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the certification. Empty while the entry is a draft, which isn't printed." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "issuer", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "publications", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "description": "The title of the publication. Empty while the entry is a draft, which isn't printed." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "title", + "publisher", + "date", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "volunteer", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "description": "The name of the organization or company. Empty while the entry is a draft, which isn't printed." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company, as text. Written from `dates` on every save; write `dates` instead." + }, + "dates": { + "type": "object", + "properties": { + "start": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it started, or the date itself for single-date entries (awards, certifications, publications)." + }, + "end": { + "anyOf": [ + { + "type": "string", + "pattern": "^\\d{4}(?:-(?:0[1-9]|1[0-2]))?$", + "description": "A year (\"2022\") or a year and month (\"2022-03\")." + }, + { + "type": "null" + } + ], + "description": "When it ended. Null while ongoing or for single dates." + }, + "present": { + "type": "boolean", + "description": "Whether it's ongoing, printed as 'Present'." + }, + "raw": { + "type": "string", + "description": "The original text when it couldn't be read exactly, such as 'Summer 2016'. It's printed as written until the dates are edited, which removes it." + } + }, + "required": [ + "start", + "end", + "present" + ], + "description": "Structured dates. Write these rather than the legacy text field (`period` or `date`), which is kept in sync from them." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "organization", + "location", + "period", + "website", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + }, + { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "type": "string", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon." + }, + "columns": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "showHeading": { + "type": "boolean", + "description": "Whether to show the section heading, icon, and decoration while retaining section content." + }, + "keepTogether": { + "type": "boolean", + "description": "If true, the section is kept on a single page instead of splitting across a page break." + }, + "startOnNewPage": { + "type": "boolean", + "description": "If true, the section always begins on a new page." + }, + "keywordLayout": { + "not": {} + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "const": "references", + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "description": "The name of the reference, or a note such as 'Available upon request'. Empty while the entry is a draft, which isn't printed." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + }, + "inlineLink": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": [ + "url", + "label", + "inlineLink" + ], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "name", + "position", + "website", + "phone", + "description" + ], + "additionalProperties": {} + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": [ + "title", + "icon", + "columns", + "hidden", + "keepTogether", + "startOnNewPage", + "id", + "type", + "items" + ] + } + ] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "meowth", + "onyx", + "pikachu", + "rhyhorn", + "scizor" + ], + "type": "string", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "sidebarSide": { + "enum": [ + "left", + "right" + ], + "type": "string", + "description": "Which side of the page the sidebar column sits on in two-column templates. When unset, each template uses its own side (and right-to-left resumes mirror it)." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": [ + "fullWidth", + "main", + "sidebar" + ] + }, + "description": "The pages to display in the layout." + } + }, + "required": [ + "sidebarWidth", + "pages" + ], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": [ + "a4", + "letter", + "free-form" + ], + "type": "string", + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "dateFormat": { + "enum": [ + "short", + "long", + "numeric", + "iso" + ], + "type": "string", + "description": "How dates print: 'short' (Mar 2022), 'long' (March 2022), 'numeric' (03/2022) or 'iso' (2022-03). When missing, it's read from how the dates were typed." + }, + "hideLinkUnderline": { + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, + "hideIcons": { + "type": "boolean", + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." + } + }, + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": [ + "hidden", + "circle", + "square", + "rectangle", + "rectangle-full", + "progress-bar", + "icon" + ], + "type": "string", + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": [ + "icon", + "type" + ] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": [ + "primary", + "text", + "background" + ] + } + }, + "required": [ + "level", + "colors" + ], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a supported resume font." + }, + "fontWeights": { + "type": "array", + "items": { + "enum": [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ], + "type": "string" + }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": [ + "fontFamily", + "fontWeights", + "fontSize", + "lineHeight" + ], + "description": "The typography for the headings of the resume." + }, + "hyphenation": { + "type": "boolean", + "description": "Enable automatic PDF hyphenation using the resume language. Defaults to false." + } + }, + "required": [ + "body", + "heading" + ], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "description": "Structured style rules that target semantic resume sections and slots for PDF rendering." + }, + "stylesheet": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "legacy", + "semantic" + ], + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "languageVersion": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991, + "exclusiveMinimum": 0 + }, + "text": { + "type": "string" + } + }, + "required": [ + "languageVersion", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "mode", + "source" + ], + "additionalProperties": false + }, + "check": { + "type": "object", + "properties": { + "ignored": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Check issues the author chose to ignore, by issue key (the rule code and where it applies)." + }, + "hiddenTerms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Job-posting terms the author hid from Job match as not true for them." + } + }, + "required": [ + "ignored", + "hiddenTerms" + ], + "description": "The author's Check choices for this resume. Not printed; missing until a choice is made." + } + }, + "required": [ + "template", + "layout", + "page", + "design", + "typography", + "notes" + ], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + } + }, + "required": [ + "picture", + "basics", + "summary", + "sections", + "customSections", + "metadata" + ], + "additionalProperties": {} + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "The AI returned an improperly formatted structure." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/ai/ats-reviews": { + "post": { + "operationId": "createAtsReview", + "summary": "Review extracted resume text", + "description": "Reviews the plain text extracted from a resume PDF and returns qualitative feedback: a summary, rewrite suggestions, strengths, and — when a job description is supplied — how the candidate's experience lines up with the role. Deliberately returns no score: the deterministic ATS report owns the only number in this feature. Requires authentication and AI credentials.", + "tags": [ + "AI" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "aiProviderId": { + "type": "string" + }, + "extractedText": { + "type": "string", + "minLength": 1, + "maxLength": 50000 + }, + "findings": { + "type": "array", + "maxItems": 120, + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "maxLength": 64 + }, + "severity": { + "type": "string", + "maxLength": 16 + }, + "message": { + "type": "string", + "maxLength": 300 + } + }, + "required": [ + "code", + "severity", + "message" + ] + }, + "default": [] + }, + "jobDescription": { + "type": "string", + "maxLength": 20000 + }, + "passages": { + "type": "array", + "maxItems": 120, + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 16, + "description": "The client's id for the passage, echoed back as `passageId`." + }, + "where": { + "type": "string", + "maxLength": 200, + "description": "Where the passage sits, e.g. “Experience · Lumen · bullet 2”." + }, + "text": { + "type": "string", + "maxLength": 1000 + } + }, + "required": [ + "id", + "where", + "text" + ] + }, + "default": [], + "description": "Bullets and paragraphs the review may rewrite. A suggestion that rewrites one names it in `passageId`, with the whole new passage in `rewrite`." + } + }, + "required": [ + "extractedText" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Qualitative review returned successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "suggestions": {}, + "strengths": {}, + "jdAlignment": { + "anyOf": [ + { + "type": "object", + "properties": { + "verdict": { + "type": "string" + }, + "missingConcepts": {}, + "strengths": {} + }, + "required": [ + "verdict" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "summary", + "jdAlignment" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "The AI returned an improperly formatted structure." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/ai/improvements": { + "post": { + "operationId": "createImprovement", + "summary": "Suggest a rewrite of one line", + "description": "Suggests a rewrite of one line of a resume or cover letter: a stronger verb, an added result, a shorter version, or the user's own request. Returns the new line, a short reason, and whether it states anything the input didn't, so the user can check it. Writes nothing. Requires authentication and AI credentials.", + "tags": [ + "AI" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "aiProviderId": { + "type": "string" + }, + "line": { + "type": "string", + "minLength": 1, + "maxLength": 2000 + }, + "action": { + "enum": [ + "verb", + "result", + "shorter", + "custom" + ], + "type": "string" + }, + "request": { + "type": "string", + "maxLength": 500 + }, + "where": { + "type": "string", + "maxLength": 200, + "default": "" + }, + "context": { + "type": "string", + "maxLength": 10000, + "default": "" + } + }, + "required": [ + "line", + "action" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The suggested line.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "text": { + "type": "string", + "minLength": 1 + }, + "why": { + "type": "string" + }, + "addsFacts": { + "type": "boolean" + } + }, + "required": [ + "text", + "why", + "addsFacts" + ] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_REQUEST" + }, + "status": { + "const": 400 + }, + "message": { + "type": "string", + "default": "The AI returned an improperly formatted structure." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "BAD_GATEWAY" + }, + "status": { + "const": 502 + }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + } + }, + "/files": { + "post": { + "operationId": "createFile", + "summary": "Upload a file", + "description": "Send a multipart form with a file field (maximum 10 MiB). Images become public profile pictures; other files remain owner-only. Requires authentication.", + "tags": [ + "Files" + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "contentMediaType": "*/*" + } + }, + "required": [ + "file" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The download URL. Profile images are public; other files require owner authentication." + }, + "path": { + "type": "string", + "description": "The storage path of the uploaded file." + }, + "contentType": { + "type": "string", + "description": "The MIME type of the uploaded file." + } + }, + "required": [ + "url", + "path", + "contentType" + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] + }, + "delete": { + "operationId": "deleteStoredFile", + "summary": "Delete a file", + "description": "Deletes a file from storage by its filename or path. If the filename does not start with 'uploads/', the user's picture directory is assumed. Requires authentication.", + "tags": [ + "Files" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "filename": { + "type": "string", + "minLength": 1, + "description": "The path or filename of the file to delete." + } + }, + "required": [ + "filename" + ] + } + } + } + }, + "responses": { + "200": { + "description": "The file was deleted successfully." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "FORBIDDEN" + }, + "status": { + "const": 403 + }, + "message": { + "type": "string", + "default": "You do not have permission to delete this file." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { + "const": true + }, + "code": { + "const": "NOT_FOUND" + }, + "status": { + "const": 404 + }, + "message": { + "type": "string", + "default": "The specified file was not found in storage." + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + }, + { + "type": "object", + "properties": { + "defined": { + "const": false + }, + "code": { + "type": "string" + }, + "status": { + "type": "number" + }, + "message": { + "type": "string" + }, + "data": {} + }, + "required": [ + "defined", + "code", + "status", + "message" + ] + } + ] + } + } + } + }, + "default": { + "description": "Structured API error. See status and code; do not branch on message text.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "defined", + "code", + "status", + "message" + ], + "properties": { + "defined": { + "type": "boolean" + }, + "code": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "data": {} + } + } + } + } + } + }, + "security": [ + { + "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] + } + ] } } }, @@ -48711,6 +100764,18 @@ }, "components": { "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "An OAuth access token issued by this instance for its API/MCP resource." + }, + "cookieAuth": { + "type": "apiKey", + "in": "cookie", + "name": "better-auth.session_token", + "description": "Browser session (secure deployments use the __Secure- prefix). Cookie requests must originate from this instance." + }, "apiKey": { "type": "apiKey", "name": "x-api-key", @@ -53384,6 +105449,12 @@ "security": [ { "apiKey": [] + }, + { + "bearerAuth": [] + }, + { + "cookieAuth": [] } ], "openapi": "3.1.1" diff --git a/knip.json b/knip.json index d9c438869..c287c577e 100644 --- a/knip.json +++ b/knip.json @@ -44,6 +44,7 @@ "bcryptjs", "cjk-regex", "css-tree", + "docx", "drizzle-zod", "fast-json-patch", "fast-png", @@ -54,6 +55,7 @@ "node-html-parser", "nodemailer", "ollama-ai-provider-v2", + "pdfjs-dist", "react-email", "react-reconciler", "resumable-stream", diff --git a/packages/api/package.json b/packages/api/package.json index ac840f62a..c35e84e4e 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -13,8 +13,9 @@ "./features/resume/public-pdf": "./src/features/resume/public-pdf.ts", "./features/resume/social-meta": "./src/features/resume/social-meta.ts", "./features/storage": "./src/features/storage/index.ts", - "./routers": "./src/routers/index.ts", - "./features/storage/transport": "./src/features/storage/transport.ts" + "./features/storage/transport": "./src/features/storage/transport.ts", + "./rest": "./src/routers/rest.ts", + "./routers": "./src/routers/index.ts" }, "scripts": { "typecheck": "tsgo --noEmit", @@ -44,7 +45,9 @@ "@reactive-resume/ai": "workspace:*", "@reactive-resume/auth": "workspace:*", "@reactive-resume/db": "workspace:*", + "@reactive-resume/docx": "workspace:*", "@reactive-resume/env": "workspace:*", + "@reactive-resume/import": "workspace:*", "@reactive-resume/pdf": "workspace:*", "@reactive-resume/resume": "workspace:*", "@reactive-resume/schema": "workspace:*", @@ -59,6 +62,7 @@ "ioredis": "catalog:", "jsonrepair": "catalog:", "ollama-ai-provider-v2": "^4.0.1", + "pdfjs-dist": "6.3.289", "resumable-stream": "^2.2.13", "sanitize-html": "^2.18.0", "sharp": "^0.35.5", diff --git a/packages/api/src/context.test.ts b/packages/api/src/context.test.ts index 992a7339c..003c5035a 100644 --- a/packages/api/src/context.test.ts +++ b/packages/api/src/context.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from "vitest"; +import { env } from "@reactive-resume/env/server"; const authMock = vi.hoisted(() => ({ api: { @@ -75,3 +76,31 @@ describe("resolveUserFromRequestHeaders", () => { expect(user).toMatchObject({ id: "user-bearer", name: "Bob" }); }); }); + +describe("cookie request origins", () => { + it.each([{ origin: "https://attacker.example" }, { origin: "null" }, { "sec-fetch-site": "cross-site" }])( + "rejects ambient credentials from another origin: %j", + async (originHeaders) => { + reset(); + authMock.api.getSession.mockResolvedValue({ user: { id: "owner" } }); + const headers = new Headers({ cookie: "session=valid", ...originHeaders }); + await expect(resolveUserFromRequestHeaders(headers)).rejects.toMatchObject({ code: "FORBIDDEN" }); + expect(authMock.api.getSession).not.toHaveBeenCalled(); + }, + ); + + it("accepts same-origin cookies and explicit bearer credentials from other origins", async () => { + reset(); + authMock.api.getSession.mockResolvedValue({ user: { id: "session-user" } }); + await expect( + resolveUserFromRequestHeaders(new Headers({ cookie: "session=valid", origin: new URL(env.APP_URL).origin })), + ).resolves.toMatchObject({ id: "session-user" }); + verifyOAuthTokenMock.mockResolvedValueOnce({ sub: "token-user" }); + setupDbResolves({ id: "token-user" }); + await expect( + resolveUserFromRequestHeaders( + new Headers({ authorization: "Bearer token", cookie: "session=valid", origin: "https://client.example" }), + ), + ).resolves.toMatchObject({ id: "token-user" }); + }); +}); diff --git a/packages/api/src/context.ts b/packages/api/src/context.ts index c3b3ff707..a8426af98 100644 --- a/packages/api/src/context.ts +++ b/packages/api/src/context.ts @@ -5,6 +5,7 @@ import { eq } from "drizzle-orm"; import { auth, verifyOAuthToken } from "@reactive-resume/auth/config"; import { db } from "@reactive-resume/db/client"; import { user } from "@reactive-resume/db/schema"; +import { env } from "@reactive-resume/env/server"; interface ORPCContext { locale: Locale; @@ -72,29 +73,44 @@ export async function resolveUserFromRequestHeaders(headers: Headers): Promise(); -export const publicProcedure = base.use(async ({ context, next }) => { - const user = await resolveUserFromRequestHeaders(context.reqHeaders); +export const publicProcedure = base + .route({ spec: (operation) => ({ ...operation, security: [] }) }) + .use(async ({ context, next }) => { + const user = await resolveUserFromRequestHeaders(context.reqHeaders); - return next({ - context: { - ...context, - user, - }, + return next({ + context: { + ...context, + user, + }, + }); }); -}); -export const protectedProcedure = publicProcedure.use(({ context, next }) => { - if (!context.user) throw new ORPCError("UNAUTHORIZED"); +export const protectedProcedure = publicProcedure + .route({ + spec: (operation) => ({ ...operation, security: [{ apiKey: [] }, { bearerAuth: [] }, { cookieAuth: [] }] }), + }) + .use(({ context, next }) => { + if (!context.user) throw new ORPCError("UNAUTHORIZED"); - return next({ - context: { - ...context, - user: context.user, - }, + return next({ + context: { + ...context, + user: context.user, + }, + }); }); -}); diff --git a/packages/api/src/dto/agent.ts b/packages/api/src/dto/agent.ts new file mode 100644 index 000000000..c31bbc1c8 --- /dev/null +++ b/packages/api/src/dto/agent.ts @@ -0,0 +1,50 @@ +import type { UIMessage } from "ai"; +import z from "zod"; + +// AI SDK message parts are an extensible protocol (including dynamic tools and provider metadata). +// Preserve their fields while documenting the stable envelope. +export const uiMessageSchema = z.object({ + id: z.string(), + role: z.enum(["system", "user", "assistant"]), + parts: z.array(z.looseObject({ type: z.string() })), + metadata: z.unknown().optional(), +}) as z.ZodType; + +export const agentThreadSchema = z.object({ + id: z.string(), + title: z.string(), + status: z.string(), + sourceResumeId: z.string().nullable(), + workingResumeId: z.string().nullable(), + coverLetterId: z.string().nullable(), + aiProviderId: z.string().nullable(), + resumeName: z.string().nullable(), + coverLetterName: z.string().nullable(), + providerLabel: z.string().nullable(), + editsProposed: z.number(), + editsAccepted: z.number(), + activeRunId: z.string().nullable(), + lastMessageAt: z.date(), + archivedAt: z.date().nullable(), + deletedAt: z.date().nullable(), + createdAt: z.date(), + updatedAt: z.date(), +}); +export const agentAttachmentSchema = z.object({ + id: z.string(), + threadId: z.string(), + messageId: z.string().nullable(), + filename: z.string(), + mediaType: z.string(), + size: z.number(), + createdAt: z.date(), +}); +export const agentConversationSchema = z.object({ + thread: agentThreadSchema, + messages: z.array(uiMessageSchema), + attachments: z.array(agentAttachmentSchema), + document: z + .object({ kind: z.enum(["resume", "letter"]), id: z.string(), name: z.string(), locked: z.boolean() }) + .nullable(), + isReadOnly: z.boolean(), +}); diff --git a/packages/api/src/dto/ai-provider.ts b/packages/api/src/dto/ai-provider.ts new file mode 100644 index 000000000..5d7ce99a4 --- /dev/null +++ b/packages/api/src/dto/ai-provider.ts @@ -0,0 +1,20 @@ +import z from "zod"; +import { aiProviderSchema } from "@reactive-resume/ai/types"; + +export const aiProviderResponseSchema = z.object({ + managed: z.boolean(), + id: z.string(), + label: z.string(), + provider: aiProviderSchema, + model: z.string(), + baseURL: z.string().nullable(), + enabled: z.boolean(), + testStatus: z.string(), + testError: z.string().nullable(), + apiKeyPreview: z.string(), + apiKeyFingerprint: z.string(), + lastTestedAt: z.date().nullable(), + lastUsedAt: z.date().nullable(), + createdAt: z.date(), + updatedAt: z.date(), +}); diff --git a/packages/api/src/dto/application.ts b/packages/api/src/dto/application.ts index 0bfda330e..fd92f7e08 100644 --- a/packages/api/src/dto/application.ts +++ b/packages/api/src/dto/application.ts @@ -11,6 +11,7 @@ import { interviewKindSchema, postingSourceSchema, } from "@reactive-resume/schema/applications/data"; +import { paginationShape } from "../pagination"; const MAX_APPLICATION_JOB_DESCRIPTION_CHARS = 20_000; const MAX_APPLICATION_DOCUMENT_BYTES = 10 * 1024 * 1024; @@ -119,6 +120,7 @@ export const applicationDto = { list: { input: z .object({ + ...paginationShape, status: applicationStatusSchema.optional(), tags: z.array(z.string()).optional(), }) diff --git a/packages/api/src/dto/cover-letter.ts b/packages/api/src/dto/cover-letter.ts index c0d426c7f..2efd9e901 100644 --- a/packages/api/src/dto/cover-letter.ts +++ b/packages/api/src/dto/cover-letter.ts @@ -8,6 +8,7 @@ import { coverLetterStyleSchema, } from "@reactive-resume/schema/cover-letter/data"; import { templateSchema } from "@reactive-resume/schema/templates"; +import { paginationShape } from "../pagination"; const idSchema = z.object({ id: z.string().min(1) }); const revisionSchema = idSchema.extend({ expectedRevision: z.number().int().min(1) }); @@ -96,7 +97,7 @@ export const coverLetterDto = { }), output: coverLetterSchema, }, - listVersions: { input: idSchema, output: z.array(letterVersionSummarySchema) }, + listVersions: { input: idSchema.extend(paginationShape), output: z.array(letterVersionSummarySchema) }, getVersion: { input: versionRefSchema, output: letterVersionSchema }, createVersion: { input: idSchema.extend({ name: versionNameSchema }), output: letterVersionSummarySchema }, renameVersion: { input: versionRefSchema.extend({ name: versionNameSchema }), output: letterVersionSummarySchema }, diff --git a/packages/api/src/dto/documents.ts b/packages/api/src/dto/documents.ts index c049ceb9e..bd4ed4602 100644 --- a/packages/api/src/dto/documents.ts +++ b/packages/api/src/dto/documents.ts @@ -1,4 +1,5 @@ import z from "zod"; +import { paginationShape } from "../pagination"; const documentTypeSchema = z.enum(["resume", "letter"]).describe("resume, or letter (a saved cover letter)."); const documentRefSchema = z.object({ @@ -26,7 +27,10 @@ export type DocumentSummary = z.infer; export const documentsDto = { list: { input: z - .object({ trashed: z.boolean().default(false).describe("List the documents in Trash instead.") }) + .object({ + ...paginationShape, + trashed: z.boolean().default(false).describe("List the documents in Trash instead."), + }) .default({ trashed: false }), output: z.array(documentSchema), }, diff --git a/packages/api/src/dto/pdf-check.ts b/packages/api/src/dto/pdf-check.ts new file mode 100644 index 000000000..c7829fe96 --- /dev/null +++ b/packages/api/src/dto/pdf-check.ts @@ -0,0 +1,79 @@ +import z from "zod"; +import { PDF_ATS_RULE_CODES } from "@reactive-resume/resume/ats-pdf"; +const severity = z.enum(["blocker", "warning", "tip"]); +const category = z.enum(["parseability", "layout", "sections", "contact", "dates", "content"]); +const code = z.enum(PDF_ATS_RULE_CODES); +const finding = z.object({ + code, + severity, + category, + params: z.record(z.string(), z.union([z.string(), z.number()])).optional(), + evidence: z + .object({ + snippet: z.string().optional(), + page: z.number().optional(), + box: z.object({ x: z.number(), y: z.number(), width: z.number(), height: z.number() }).optional(), + }) + .optional(), +}); +export const jobTermSchema = z.object({ + term: z.string(), + jdCount: z.number(), + resumeCount: z.number(), + weight: z.number(), +}); +const jd = z.object({ + terms: z.array(jobTermSchema).readonly(), + matchedTerms: z.array(z.string()).readonly(), + missingTerms: z.array(z.string()).readonly(), + totalTerms: z.number(), + matchedCount: z.number(), + weightedCoverage: z.number(), + stuffedTerms: z.array(z.string()).readonly(), + documentHasHiddenText: z.boolean(), +}); +export const pdfCheckSchema = z.object({ + version: z.literal(1), + score: z.number(), + cappedBy: z.array(code).readonly(), + categories: z + .array( + z.object({ + category: category.exclude(["content"]), + score: z.number(), + weight: z.number(), + applicableChecks: z.number(), + passedChecks: z.number(), + skippedChecks: z.number(), + }), + ) + .readonly(), + checks: z + .array( + z.object({ + code, + category, + severity, + status: z.enum(["pass", "skip", "fail"]), + skipReason: z + .enum(["no-text", "no-operators", "not-english", "not-applicable", "encrypted", "insufficient-data"]) + .optional(), + findings: z.array(finding).readonly(), + }), + ) + .readonly(), + findings: z.array(finding).readonly(), + tips: z.array(finding).readonly(), + counts: z.object({ blocker: z.number(), warning: z.number(), tip: z.number() }), + applicableChecks: z.number(), + passedChecks: z.number(), + skippedChecks: z.number(), + jd: jd.nullable(), + file: z.object({ name: z.string(), sizeBytes: z.number(), magicBytesOk: z.boolean() }), + document: z.object({ + pageCount: z.number(), + truncated: z.boolean(), + wordCount: z.number(), + operatorsAvailable: z.boolean(), + }), +}); diff --git a/packages/api/src/dto/resume.ts b/packages/api/src/dto/resume.ts index f58fbc5e7..80612b3fa 100644 --- a/packages/api/src/dto/resume.ts +++ b/packages/api/src/dto/resume.ts @@ -4,6 +4,7 @@ import * as schema from "@reactive-resume/db/schema"; import { jsonPatchOperationSchema } from "@reactive-resume/resume/patch"; import { resumeDataSchema } from "@reactive-resume/schema/resume/data"; import { writableResumeDataSchema } from "@reactive-resume/schema/resume/write"; +import { paginationShape } from "../pagination"; const resumeSchema = createSelectSchema(schema.resume, { id: z.string().describe("The ID of the resume."), @@ -38,6 +39,7 @@ export const resumeDto = { list: { input: z .object({ + ...paginationShape, tags: z .union([z.string().transform((tag) => [tag]), z.array(z.string())]) .optional() @@ -168,7 +170,10 @@ export const resumeDto = { }, listVersions: { - input: z.object({ resumeId: z.string().describe("The ID of the resume whose version history to list.") }), + input: z.object({ + ...paginationShape, + resumeId: z.string().describe("The ID of the resume whose version history to list."), + }), output: z.array(versionSchema), }, diff --git a/packages/api/src/features/agent/attachments.ts b/packages/api/src/features/agent/attachments.ts index c0be91e98..56b64910a 100644 --- a/packages/api/src/features/agent/attachments.ts +++ b/packages/api/src/features/agent/attachments.ts @@ -1,5 +1,6 @@ import z from "zod"; import { protectedProcedure } from "../../context"; +import { agentAttachmentSchema } from "../../dto/agent"; import { storageUploadRateLimit } from "../../middleware/rate-limit"; import { mapAgentEnvironmentError } from "./routing"; import { agentService } from "./service"; @@ -18,9 +19,13 @@ export const attachmentsRouter = { threadId: z.string(), filename: z.string().trim().min(1), mediaType: z.string().trim().min(1), - data: z.string().min(1), + data: z + .base64() + .min(1) + .max(4 * Math.ceil((25 * 1024 * 1024) / 3)), }), ) + .output(agentAttachmentSchema) .use(storageUploadRateLimit) .use(mapAgentEnvironmentError) .handler(({ context, input }) => diff --git a/packages/api/src/features/agent/messages.ts b/packages/api/src/features/agent/messages.ts index 25aae1545..59642165d 100644 --- a/packages/api/src/features/agent/messages.ts +++ b/packages/api/src/features/agent/messages.ts @@ -1,8 +1,9 @@ -import type { UIMessage } from "ai"; +import { eventIterator } from "@orpc/server"; import z from "zod"; import { protectedProcedure } from "../../context"; +import { uiMessageSchema } from "../../dto/agent"; import { aiRequestRateLimit } from "../../middleware/rate-limit"; -import { isUiMessage, mapAgentEnvironmentError } from "./routing"; +import { mapAgentEnvironmentError } from "./routing"; import { agentService } from "./service"; export const messagesRouter = { @@ -17,7 +18,7 @@ export const messagesRouter = { .input( z.object({ threadId: z.string(), - message: z.custom(isUiMessage, { message: "Invalid UI message." }), + message: uiMessageSchema, attachmentIds: z.array(z.string().trim().min(1)).max(10).optional(), // The context chips: leaving one out keeps it out of what's sent. context: z @@ -31,6 +32,7 @@ export const messagesRouter = { ) .use(aiRequestRateLimit) .use(mapAgentEnvironmentError) + .output(eventIterator(z.string())) .handler(({ context, input }) => agentService.messages.send({ userId: context.user.id, @@ -54,7 +56,7 @@ export const messagesRouter = { threadId: z.string(), // Deprecated and ignored: partial content now persists server-side via the run's // abort path. Kept in the schema for one release so mid-deploy clients still parse. - partialMessage: z.custom(isUiMessage, { message: "Invalid UI message." }).optional(), + partialMessage: uiMessageSchema.optional(), }), ) .output(z.void()) @@ -76,6 +78,7 @@ export const messagesRouter = { }) .input(z.object({ threadId: z.string() })) .use(mapAgentEnvironmentError) + .output(eventIterator(z.string())) .handler(({ context, input }) => agentService.messages.resume({ userId: context.user.id, threadId: input.threadId }), ), diff --git a/packages/api/src/features/agent/routing.ts b/packages/api/src/features/agent/routing.ts index f287e1df6..9d978dae5 100644 --- a/packages/api/src/features/agent/routing.ts +++ b/packages/api/src/features/agent/routing.ts @@ -1,5 +1,4 @@ import type { AnyMiddleware } from "@orpc/server"; -import type { UIMessage } from "ai"; import { ORPCError } from "@orpc/client"; function isAgentEnvironmentUnavailable(error: unknown) { @@ -12,17 +11,6 @@ function throwUnavailable(): never { }); } -export function isUiMessage(value: unknown): value is UIMessage { - if (!value || typeof value !== "object") return false; - - const message = value as Partial; - return ( - typeof message.id === "string" && - (message.role === "system" || message.role === "user" || message.role === "assistant") && - Array.isArray(message.parts) - ); -} - // ponytail: single middleware replaces 12 near-identical try/catch blocks across agent route handlers export const mapAgentEnvironmentError: AnyMiddleware = async ({ next }) => { try { diff --git a/packages/api/src/features/agent/threads.ts b/packages/api/src/features/agent/threads.ts index 0773d0de1..6c44efe12 100644 --- a/packages/api/src/features/agent/threads.ts +++ b/packages/api/src/features/agent/threads.ts @@ -1,5 +1,7 @@ import z from "zod"; import { protectedProcedure } from "../../context"; +import { agentThreadSchema, agentConversationSchema } from "../../dto/agent"; +import { paginate, paginationShape } from "../../pagination"; import { mapAgentEnvironmentError } from "./routing"; import { agentService } from "./service"; @@ -13,7 +15,11 @@ export const threadsRouter = { summary: "List agent threads", }) .use(mapAgentEnvironmentError) - .handler(({ context }) => agentService.threads.list({ userId: context.user.id })), + .output(z.array(agentThreadSchema)) + .input(z.object(paginationShape).default({})) + .handler(async ({ context, input }) => + paginate(await agentService.threads.list({ userId: context.user.id }), input, context.resHeaders), + ), start: protectedProcedure .route({ @@ -37,6 +43,7 @@ export const threadsRouter = { }), ) .use(mapAgentEnvironmentError) + .output(agentThreadSchema) .handler(({ context, input }) => agentService.threads.start({ userId: context.user.id, ...input })), get: protectedProcedure @@ -49,6 +56,7 @@ export const threadsRouter = { }) .input(z.object({ id: z.string() })) .use(mapAgentEnvironmentError) + .output(agentConversationSchema) .handler(({ context, input }) => agentService.threads.get({ id: input.id, userId: context.user.id })), update: protectedProcedure @@ -61,6 +69,7 @@ export const threadsRouter = { }) .input(z.object({ id: z.string(), aiProviderId: z.string().min(1) })) .use(mapAgentEnvironmentError) + .output(agentThreadSchema) .handler(({ context, input }) => agentService.threads.update({ id: input.id, userId: context.user.id, aiProviderId: input.aiProviderId }), ), diff --git a/packages/api/src/features/ai-providers/router.ts b/packages/api/src/features/ai-providers/router.ts index cf55431c1..e7014acc2 100644 --- a/packages/api/src/features/ai-providers/router.ts +++ b/packages/api/src/features/ai-providers/router.ts @@ -1,9 +1,9 @@ -import type { AiProviderResponse } from "./service"; import { ORPCError } from "@orpc/client"; -import { type } from "@orpc/server"; import z from "zod"; import { protectedProcedure } from "../../context"; +import { aiProviderResponseSchema } from "../../dto/ai-provider"; import { aiRequestRateLimit } from "../../middleware/rate-limit"; +import { paginate, paginationShape } from "../../pagination"; import { providerInput, updateProviderInput } from "./inputs"; import { aiProvidersService } from "./service"; @@ -25,11 +25,14 @@ export const aiProvidersRouter = { summary: "List saved AI providers", description: "Lists saved provider/model/API key combinations for the authenticated user. API keys are redacted.", }) - .output(type()) + .output(z.array(aiProviderResponseSchema)) .errors({ PRECONDITION_FAILED: { message: "AI agent workspace is not configured.", status: 412 }, }) - .handler(({ context }) => aiProvidersService.list({ userId: context.user.id })), + .input(z.object(paginationShape).default({})) + .handler(async ({ context, input }) => + paginate(await aiProvidersService.list({ userId: context.user.id }), input, context.resHeaders), + ), create: protectedProcedure .route({ @@ -41,7 +44,7 @@ export const aiProvidersRouter = { description: "Stores an encrypted provider/model/API key combination. The key is never returned.", }) .input(providerInput) - .output(type()) + .output(aiProviderResponseSchema) .errors({ BAD_REQUEST: { message: "Invalid AI provider configuration.", status: 400 }, FORBIDDEN: { message: "AI is managed by the server.", status: 403 }, @@ -74,7 +77,7 @@ export const aiProvidersRouter = { "Updates a saved provider/model/API key combination. Updating the key requires retesting before use.", }) .input(updateProviderInput) - .output(type()) + .output(aiProviderResponseSchema) .errors({ BAD_REQUEST: { message: "Invalid AI provider configuration.", status: 400 }, FORBIDDEN: { message: "AI is managed by the server.", status: 403 }, @@ -126,7 +129,7 @@ export const aiProvidersRouter = { description: "Decrypts the saved API key server-side and validates the provider/model connection.", }) .input(z.object({ id: z.string() })) - .output(type()) + .output(aiProviderResponseSchema) .use(aiRequestRateLimit) .errors({ BAD_REQUEST: { message: "Invalid AI provider configuration.", status: 400 }, diff --git a/packages/api/src/features/ai/router.ts b/packages/api/src/features/ai/router.ts index c0b6c2039..3af32fa70 100644 --- a/packages/api/src/features/ai/router.ts +++ b/packages/api/src/features/ai/router.ts @@ -1,7 +1,7 @@ -import type { ResumeData } from "@reactive-resume/schema/resume/data"; import { ORPCError } from "@orpc/client"; import { AISDKError } from "ai"; import { flattenError, ZodError, z } from "zod"; +import { resumeDataSchema } from "@reactive-resume/schema/resume/data"; import { protectedProcedure } from "../../context"; import { aiRequestRateLimit } from "../../middleware/rate-limit"; import { aiProvidersService } from "../ai-providers/service"; @@ -57,7 +57,8 @@ export const aiRouter = { .input(z.object({ aiProviderId: z.string().optional(), file: fileInputSchema })) .use(aiRequestRateLimit) .errors(aiErrors) - .handler(async ({ context, input }): Promise => { + .output(resumeDataSchema) + .handler(async ({ context, input }) => { try { const provider = await getRunnableProvider(context.user.id, input.aiProviderId); return await aiService.parsePdf({ @@ -95,6 +96,7 @@ export const aiRouter = { ) .use(aiRequestRateLimit) .errors(aiErrors) + .output(resumeDataSchema) .handler(async ({ context, input }) => { try { const provider = await getRunnableProvider(context.user.id, input.aiProviderId); diff --git a/packages/api/src/features/applications/crud.ts b/packages/api/src/features/applications/crud.ts index e2dc3ae91..62d88eda8 100644 --- a/packages/api/src/features/applications/crud.ts +++ b/packages/api/src/features/applications/crud.ts @@ -1,6 +1,8 @@ +import z from "zod"; import { protectedProcedure } from "../../context"; import { applicationDto } from "../../dto/application"; import { resumeMutationRateLimit } from "../../middleware/rate-limit"; +import { paginate, paginationShape } from "../../pagination"; import { applicationService } from "./service"; export const crudRouter = { @@ -17,12 +19,16 @@ export const crudRouter = { }) .input(applicationDto.list.input) .output(applicationDto.list.output) - .handler(({ input, context }) => - applicationService.list({ - userId: context.user.id, - ...(input.status ? { status: input.status } : {}), - ...(input.tags ? { tags: input.tags } : {}), - }), + .handler(async ({ input, context }) => + paginate( + await applicationService.list({ + userId: context.user.id, + ...(input.status ? { status: input.status } : {}), + ...(input.tags ? { tags: input.tags } : {}), + }), + input, + context.resHeaders, + ), ), getById: protectedProcedure @@ -305,5 +311,8 @@ export const crudRouter = { successDescription: "Distinct tags.", }) .output(applicationDto.tags.output) - .handler(({ context }) => applicationService.listTags({ userId: context.user.id })), + .input(z.object(paginationShape).default({})) + .handler(async ({ context, input }) => + paginate(await applicationService.listTags({ userId: context.user.id }), input, context.resHeaders), + ), }; diff --git a/packages/api/src/features/auth/router.ts b/packages/api/src/features/auth/router.ts index c42a3d7b5..5b0d4af0e 100644 --- a/packages/api/src/features/auth/router.ts +++ b/packages/api/src/features/auth/router.ts @@ -1,5 +1,10 @@ -import type { ProviderList } from "./service"; +import { createSelectSchema } from "drizzle-zod"; +import z from "zod"; +import * as schema from "@reactive-resume/db/schema"; +import { coverLetterSchema } from "@reactive-resume/schema/cover-letter/data"; import { protectedProcedure, publicProcedure } from "../../context"; +import { applicationDto } from "../../dto/application"; +import { resumeDto } from "../../dto/resume"; import { authService } from "./service"; export const authRouter = { @@ -15,7 +20,9 @@ export const authRouter = { "Returns a list of all authentication providers enabled on this Reactive Resume instance, along with their display names. Possible providers include password-based credentials, Google, GitHub, LinkedIn, and custom OAuth. No authentication required.", successDescription: "A map of enabled authentication provider identifiers to their display names.", }) - .handler((): ProviderList => authService.providers.list()), + .input(z.object({}).optional()) + .output(z.partialRecord(z.enum(["credential", "passkey", "google", "github", "linkedin", "custom"]), z.string())) + .handler(() => authService.providers.list()), }, exportData: protectedProcedure @@ -29,6 +36,26 @@ export const authRouter = { "Returns a JSON-serializable export of the authenticated user's data, including their public profile fields, resumes, independent cover letters and job applications. Images remain URL references. Secrets such as password hashes, tokens, and API keys are never included. Requires authentication.", successDescription: "The user's exported account data.", }) + .input(z.object({}).optional()) + .output( + z.object({ + exportedAt: z.string(), + user: createSelectSchema(schema.user).pick({ + id: true, + name: true, + email: true, + username: true, + displayUsername: true, + image: true, + emailVerified: true, + createdAt: true, + updatedAt: true, + }), + resumes: z.array(resumeDto.getById.output.omit({ hasPassword: true, applicationId: true })), + coverLetters: z.array(coverLetterSchema), + applications: z.array(applicationDto.getById.output), + }), + ) .handler(({ context }) => authService.exportData({ userId: context.user.id })), deleteAccount: protectedProcedure @@ -42,5 +69,7 @@ export const authRouter = { "Permanently deletes the authenticated user's account, including all resumes, uploaded files (profile pictures, screenshots, PDFs), and associated data. This action is irreversible. Requires authentication.", successDescription: "The user account and all associated data have been successfully deleted.", }) + .input(z.object({}).optional()) + .output(z.void()) .handler(({ context }) => authService.deleteAccount({ userId: context.user.id })), }; diff --git a/packages/api/src/features/auth/service.ts b/packages/api/src/features/auth/service.ts index e180593dd..c794f20fb 100644 --- a/packages/api/src/features/auth/service.ts +++ b/packages/api/src/features/auth/service.ts @@ -8,7 +8,7 @@ import { env } from "@reactive-resume/env/server"; import { coverLetterService } from "../cover-letters/service"; import { getStorageService } from "../storage/service"; -export type ProviderList = Partial>; +type ProviderList = Partial>; const providers = { list: (): ProviderList => { diff --git a/packages/api/src/features/cover-letters/router.ts b/packages/api/src/features/cover-letters/router.ts index 36fb29810..f1451b27c 100644 --- a/packages/api/src/features/cover-letters/router.ts +++ b/packages/api/src/features/cover-letters/router.ts @@ -2,7 +2,8 @@ import { eventIterator } from "@orpc/server"; import z from "zod"; import { protectedProcedure } from "../../context"; import { coverLetterDto } from "../../dto/cover-letter"; -import { aiRequestRateLimit } from "../../middleware/rate-limit"; +import { aiRequestRateLimit, resumeMutationRateLimit } from "../../middleware/rate-limit"; +import { paginate } from "../../pagination"; import { draftLetterBody } from "./draft"; import { coverLetterService } from "./service"; import { deleteLetterVersion, getLetterVersion, listLetterVersions, renameLetterVersion } from "./versions"; @@ -46,6 +47,7 @@ export const coverLettersRouter = { successDescription: "The newly created cover letter.", }) .input(coverLetterDto.create.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.create.output) .handler(({ context, input }) => coverLetterService.create({ ...input, userId: context.user.id })), update: protectedProcedure @@ -59,6 +61,7 @@ export const coverLettersRouter = { successDescription: "The updated cover letter.", }) .input(coverLetterDto.update.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.update.output) .handler(({ context, input }) => coverLetterService.update({ ...input, userId: context.user.id })), refreshStyle: protectedProcedure @@ -72,6 +75,7 @@ export const coverLettersRouter = { successDescription: "The cover letter with refreshed style.", }) .input(coverLetterDto.refreshStyle.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.refreshStyle.output) .handler(({ context, input }) => coverLetterService.refreshStyle({ ...input, userId: context.user.id })), duplicate: protectedProcedure @@ -85,6 +89,7 @@ export const coverLettersRouter = { successDescription: "The duplicated cover letter.", }) .input(coverLetterDto.duplicate.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.duplicate.output) .handler(({ context, input }) => coverLetterService.duplicate({ ...input, userId: context.user.id })), delete: protectedProcedure @@ -99,6 +104,7 @@ export const coverLettersRouter = { successDescription: "The cover letter is in Trash.", }) .input(coverLetterDto.delete.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.delete.output) .handler(({ context, input }) => coverLetterService.delete({ ...input, userId: context.user.id })), export: protectedProcedure @@ -125,6 +131,7 @@ export const coverLettersRouter = { successDescription: "The imported cover letter.", }) .input(coverLetterDto.import.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.import.output) .handler(({ context, input }) => coverLetterService.import({ ...input, userId: context.user.id })), listVersions: protectedProcedure @@ -140,7 +147,13 @@ export const coverLettersRouter = { }) .input(coverLetterDto.listVersions.input) .output(coverLetterDto.listVersions.output) - .handler(({ context, input }) => listLetterVersions({ coverLetterId: input.id, userId: context.user.id })), + .handler(async ({ context, input }) => + paginate( + await listLetterVersions({ coverLetterId: input.id, userId: context.user.id }), + input, + context.resHeaders, + ), + ), getVersion: protectedProcedure .route({ method: "GET", @@ -167,6 +180,7 @@ export const coverLettersRouter = { successDescription: "The new version.", }) .input(coverLetterDto.createVersion.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.createVersion.output) .handler(({ context, input }) => coverLetterService.createVersion({ ...input, userId: context.user.id })), renameVersion: protectedProcedure @@ -180,6 +194,7 @@ export const coverLettersRouter = { successDescription: "The renamed version.", }) .input(coverLetterDto.renameVersion.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.renameVersion.output) .handler(({ context, input }) => renameLetterVersion({ @@ -200,6 +215,7 @@ export const coverLettersRouter = { successDescription: "The version was deleted.", }) .input(coverLetterDto.deleteVersion.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.deleteVersion.output) .handler(({ context, input }) => deleteLetterVersion({ coverLetterId: input.id, userId: context.user.id, versionId: input.versionId }), @@ -216,6 +232,7 @@ export const coverLettersRouter = { successDescription: "The restored letter.", }) .input(coverLetterDto.restoreVersion.input) + .use(resumeMutationRateLimit) .output(coverLetterDto.restoreVersion.output) .handler(({ context, input }) => coverLetterService.restoreVersion({ ...input, userId: context.user.id })), draft: protectedProcedure diff --git a/packages/api/src/features/documents/exports.test.ts b/packages/api/src/features/documents/exports.test.ts new file mode 100644 index 000000000..eebb08468 --- /dev/null +++ b/packages/api/src/features/documents/exports.test.ts @@ -0,0 +1,34 @@ +import { beforeEach, expect, it, vi } from "vitest"; +import { call } from "@orpc/server"; +import { unzipSync, strFromU8 } from "fflate"; +import { defaultResumeData } from "@reactive-resume/schema/resume/default"; + +const getResume = vi.hoisted(() => vi.fn()); +vi.mock("../resume/service", () => ({ resumeService: { getById: getResume } })); +vi.mock("@reactive-resume/auth/config", () => ({ + auth: { api: { getSession: vi.fn().mockResolvedValue({ user: { id: "owner" } }) } }, + verifyOAuthToken: vi.fn(), +})); +const { documentExports } = await import("./exports"); +const context = { locale: "en-US" as const, reqHeaders: new Headers() }; +beforeEach(() => { + const data = structuredClone(defaultResumeData); + data.basics.name = "Ada Lovelace"; + data.summary.content = "

Builds analytical engines.

"; + getResume.mockResolvedValue({ id: "resume-1", name: "Ada", data }); +}); + +it("exports saved data as round-trippable JSON, readable Markdown and a Word document", async () => { + const json = await call(documentExports.resume, { id: "resume-1", format: "json" }, { context }); + expect(JSON.parse(await json.body.text())).toMatchObject({ basics: { name: "Ada Lovelace" } }); + expect(json.headers["content-disposition"]).toContain(".json"); + const markdown = await call(documentExports.resume, { id: "resume-1", format: "md" }, { context }); + expect(await markdown.body.text()).toContain("Builds analytical engines."); + const docx = await call(documentExports.resume, { id: "resume-1", format: "docx" }, { context }); + const files = unzipSync(new Uint8Array(await docx.body.arrayBuffer())); + const xml = files["word/document.xml"]; + expect(xml).toBeDefined(); + if (!xml) throw new Error("Missing Word document"); + expect(strFromU8(xml)).toContain("Ada Lovelace"); + expect(getResume).toHaveBeenCalledWith({ id: "resume-1", userId: "owner" }); +}); diff --git a/packages/api/src/features/documents/exports.ts b/packages/api/src/features/documents/exports.ts new file mode 100644 index 000000000..b1b24fcc4 --- /dev/null +++ b/packages/api/src/features/documents/exports.ts @@ -0,0 +1,127 @@ +import type { ResumeData } from "@reactive-resume/schema/resume/data"; +import { ORPCError } from "@orpc/server"; +import z from "zod"; +import { env } from "@reactive-resume/env/server"; +import { composeCoverLetter, createCoverLetterResumeData } from "@reactive-resume/resume/cover-letter"; +import { getResumeExportData } from "@reactive-resume/resume/export-sections"; +import { buildMarkdown } from "@reactive-resume/resume/markdown"; +import { generateFilename } from "@reactive-resume/utils/file"; +import { protectedProcedure } from "../../context"; +import { pdfExportRateLimit } from "../../middleware/rate-limit"; +import { coverLetterService } from "../cover-letters/service"; +import { resumeService } from "../resume/service"; + +const exportInput = z.object({ + id: z.string().min(1), + format: z.enum(["pdf", "docx", "md", "json"]), +}); +const exportOutput = z.object({ headers: z.object({ "content-disposition": z.string() }), body: z.file() }); + +async function fileResponse( + data: ResumeData, + name: string, + format: z.infer["format"], + json: unknown, + includeCoverLetterHeader = false, +) { + const filename = generateFilename(name, format); + let body: File; + try { + if (format === "json") body = new File([JSON.stringify(json, null, 2)], filename, { type: "application/json" }); + else if (format === "pdf") { + const { createResumePdfFile } = await import("@reactive-resume/pdf/server"); + body = await createResumePdfFile({ + data, + filename, + uploadOrigin: env.APP_URL, + renderOptions: { includeCoverLetterHeader }, + }); + } else { + const { getResumeSectionTitle } = await import("@reactive-resume/pdf/section-title"); + const resolveTitle = (id: string) => getResumeSectionTitle(data, id); + if (format === "md") body = new File([buildMarkdown(data, resolveTitle)], filename, { type: "text/markdown" }); + else { + const { buildDocx } = await import("@reactive-resume/docx"); + const blob = await buildDocx(data, resolveTitle); + body = new File([blob], filename, { type: blob.type }); + } + } + } catch (error) { + console.error("[document export] Rendering failed", error); + throw new ORPCError("INTERNAL_SERVER_ERROR", { message: "Could not generate the document." }); + } + return { headers: { "content-disposition": `attachment; filename="${filename}"` }, body }; +} + +export const documentExports = { + resume: protectedProcedure + .route({ + method: "GET", + path: "/resumes/{id}/exports/{format}", + tags: ["Resumes"], + operationId: "exportResumeFile", + summary: "Download a resume in PDF, DOCX, Markdown or JSON", + outputStructure: "detailed", + }) + .input(exportInput) + .output(exportOutput) + .use(pdfExportRateLimit) + .handler(async ({ context, input }) => { + const resume = await resumeService.getById({ id: input.id, userId: context.user.id }); + return fileResponse(getResumeExportData(resume.data, "resume"), resume.name, input.format, resume.data); + }), + letter: protectedProcedure + .route({ + method: "GET", + path: "/cover-letters/{id}/exports/{format}", + tags: ["Cover Letters"], + operationId: "exportCoverLetterFile", + summary: "Download a cover letter in PDF, DOCX, Markdown or JSON", + description: + "Uses live linked sender/design details. Structured letter greetings default to English; supply words to localize them. Dates use the document locale.", + outputStructure: "detailed", + }) + .input( + exportInput.extend({ + words: z + .object({ + greeting: z.string().max(500).describe("Use {name} for the recipient."), + teamGreeting: z.string().max(500), + hiringTeam: z.string().max(500), + signOff: z.string().max(500), + }) + .optional(), + }), + ) + .output(exportOutput) + .use(pdfExportRateLimit) + .handler(async ({ context, input }) => { + const letter = await coverLetterService.getById({ id: input.id, userId: context.user.id }); + const words = input.words ?? { + greeting: "Dear {name},", + teamGreeting: "Dear hiring team,", + hiringTeam: "Hiring team", + signOff: "Kind regards,", + }; + const composed = composeCoverLetter(letter, { + ...words, + greeting: (name) => words.greeting.replace("{name}", name), + formatDate: (date) => + new Date(`${date}T12:00:00Z`).toLocaleDateString(letter.style.metadata.page.locale, { + day: "numeric", + month: "long", + year: "numeric", + timeZone: "UTC", + }), + }); + const json = + input.format === "json" ? await coverLetterService.export({ id: input.id, userId: context.user.id }) : null; + return fileResponse( + createCoverLetterResumeData({ ...letter, ...composed }), + letter.name, + input.format, + json, + true, + ); + }), +}; diff --git a/packages/api/src/features/documents/router.ts b/packages/api/src/features/documents/router.ts index f722e8d8b..bee180dff 100644 --- a/packages/api/src/features/documents/router.ts +++ b/packages/api/src/features/documents/router.ts @@ -1,6 +1,8 @@ +import z from "zod"; import { protectedProcedure } from "../../context"; import { documentsDto } from "../../dto/documents"; import { resumeMutationRateLimit } from "../../middleware/rate-limit"; +import { paginate } from "../../pagination"; import { documentsService } from "./service"; const route = ( @@ -32,7 +34,13 @@ export const documentsRouter = { ) .input(documentsDto.list.input) .output(documentsDto.list.output) - .handler(({ context, input }) => documentsService.list({ userId: context.user.id, trashed: input.trashed })), + .handler(async ({ context, input }) => + paginate( + await documentsService.list({ userId: context.user.id, trashed: input.trashed }), + input, + context.resHeaders, + ), + ), counts: protectedProcedure .route( @@ -44,6 +52,7 @@ export const documentsRouter = { "Counts live resumes and letters, and everything in Trash.", ), ) + .input(z.object({}).optional()) .output(documentsDto.counts.output) .handler(({ context }) => documentsService.counts({ userId: context.user.id })), diff --git a/packages/api/src/features/flags/router.ts b/packages/api/src/features/flags/router.ts index 25a41592f..b68c6d0b5 100644 --- a/packages/api/src/features/flags/router.ts +++ b/packages/api/src/features/flags/router.ts @@ -23,6 +23,7 @@ export const flagsRouter = { "Returns the current feature flags for this Reactive Resume instance. Feature flags control instance-wide settings such as whether new user signups or email-based authentication are disabled. No authentication required.", successDescription: "The current feature flags for this instance.", }) + .input(z.object({}).optional()) .output( z.object({ disableSignups: z.boolean().describe("Whether new user signups are disabled on this instance."), diff --git a/packages/api/src/features/resume/checks.ts b/packages/api/src/features/resume/checks.ts new file mode 100644 index 000000000..2cffd4f18 --- /dev/null +++ b/packages/api/src/features/resume/checks.ts @@ -0,0 +1,109 @@ +import z from "zod"; +import { ATS_CATEGORIES, ATS_RULE_CODES, lintResumeForAts } from "@reactive-resume/resume/ats"; +import { + analyzePdfResume, + buildExtractedDocument, + matchJobDescription, + surfaceFormsOf, +} from "@reactive-resume/resume/ats-pdf"; +import { buildMarkdown } from "@reactive-resume/resume/markdown"; +import { publicProcedure, protectedProcedure } from "../../context"; +import { jobTermSchema, pdfCheckSchema } from "../../dto/pdf-check"; +import { pdfAnalysisRateLimit } from "../../middleware/rate-limit"; +import { extractPdf } from "./pdf-analysis"; +import { resumeService } from "./service"; + +const finding = z.object({ + code: z.enum(ATS_RULE_CODES), + severity: z.enum(["error", "warning", "info"]), + pointer: z.string(), + key: z.string(), + params: z.record(z.string(), z.union([z.string(), z.number()])).optional(), +}); +export const checkResume = protectedProcedure + .route({ + method: "GET", + path: "/resumes/{id}/checks", + tags: ["Resumes"], + operationId: "getResumeChecks", + summary: "Check resume content for ATS issues", + description: + "Runs the editor's deterministic contact, date, layout, heading and writing checks. Respects ignored findings saved on the resume.", + }) + .input(z.object({ id: z.string().min(1) })) + .output( + z.object({ + findings: z.array(finding).readonly(), + ignored: z.array(finding).readonly(), + counts: z.object({ error: z.number(), warning: z.number(), info: z.number() }), + totalRules: z.number(), + passedRules: z.number(), + score: z.number(), + categories: z.record(z.enum(ATS_CATEGORIES), z.object({ total: z.number(), passed: z.number() })), + }), + ) + .handler(async ({ input, context }) => + lintResumeForAts((await resumeService.getById({ id: input.id, userId: context.user.id })).data), + ); + +export const checkPdf = publicProcedure + .route({ + method: "POST", + path: "/pdf-checks", + tags: ["Checks"], + operationId: "createPdfCheck", + summary: "Check a PDF for ATS readability", + description: + "Multipart PDF upload, up to 25 MB. Processes at most 30 pages, reports truncation and skipped checks, and stores nothing. Rate limited per client address. No authentication required.", + }) + .input(z.object({ file: z.file().max(25_000_000), jobDescription: z.string().max(20_000).optional() })) + .output( + pdfCheckSchema.extend({ + fullText: z.string().describe("Extracted text in reading order, for the optional AI review."), + }), + ) + .use(pdfAnalysisRateLimit) + .handler(async ({ input, signal }) => { + const raw = await extractPdf(input.file, signal); + return { + ...analyzePdfResume(raw, input.jobDescription ? { jobDescription: input.jobDescription } : {}), + fullText: buildExtractedDocument(raw).fullText, + }; + }); + +export const matchResume = protectedProcedure + .route({ + method: "POST", + path: "/resumes/{id}/job-matches", + tags: ["Checks"], + operationId: "createResumeJobMatch", + summary: "Match resume terms to a job posting", + description: + "Runs the editor's deterministic term matching and respects terms hidden on this resume. No AI provider is needed.", + }) + .input(z.object({ id: z.string().min(1), jobDescription: z.string().trim().min(1).max(20_000) })) + .output( + z.object({ + found: z.array(jobTermSchema.extend({ label: z.string() })), + missing: z.array(jobTermSchema.extend({ label: z.string() })), + total: z.number(), + }), + ) + .handler(async ({ input, context }) => { + const { data } = await resumeService.getById({ id: input.id, userId: context.user.id }); + const hidden = new Set(data.metadata.check?.hiddenTerms ?? []); + const terms = matchJobDescription({ jobDescription: input.jobDescription, resumeText: buildMarkdown(data) }) + .terms.filter((term) => !hidden.has(term.term)) + .map((term) => ({ + ...term, + label: + surfaceFormsOf(term.term) + .map((form) => input.jobDescription.match(new RegExp(RegExp.escape(form), "i"))?.[0]) + .find(Boolean) ?? term.term, + })); + return { + found: terms.filter((term) => term.resumeCount > 0), + missing: terms.filter((term) => term.resumeCount === 0), + total: terms.length, + }; + }); diff --git a/packages/api/src/features/resume/crud.ts b/packages/api/src/features/resume/crud.ts index 9c7644aa6..afaef4540 100644 --- a/packages/api/src/features/resume/crud.ts +++ b/packages/api/src/features/resume/crud.ts @@ -2,6 +2,7 @@ import { generateId, generateRandomName } from "@reactive-resume/utils/string"; import { protectedProcedure } from "../../context"; import { resumeDto } from "../../dto/resume"; import { resumeMutationRateLimit } from "../../middleware/rate-limit"; +import { paginate } from "../../pagination"; import { documentsService } from "../documents/service"; import { createResumeData } from "./initial-data"; import { parseStoredResumeData } from "./resume-data-validation"; @@ -21,12 +22,16 @@ export const crudRouter = { }) .input(resumeDto.list.input.optional().default({ tags: [], sort: "lastUpdatedAt" })) .output(resumeDto.list.output) - .handler(({ input, context }) => - resumeService.list({ - userId: context.user.id, - tags: input.tags, - sort: input.sort, - }), + .handler(async ({ input, context }) => + paginate( + await resumeService.list({ + userId: context.user.id, + tags: input.tags, + sort: input.sort, + }), + input, + context.resHeaders, + ), ), getById: protectedProcedure @@ -144,6 +149,7 @@ export const crudRouter = { id: input.id, userId: context.user.id, ...(input.name !== undefined ? { name: input.name } : {}), + ...(input.sessionId !== undefined ? { sessionId: input.sessionId } : {}), ...(input.slug !== undefined ? { slug: input.slug } : {}), ...(input.tags !== undefined ? { tags: input.tags } : {}), ...(input.data !== undefined ? { data: input.data } : {}), diff --git a/packages/api/src/features/resume/event-router.ts b/packages/api/src/features/resume/event-router.ts index 0d46a27cb..b46ed0220 100644 --- a/packages/api/src/features/resume/event-router.ts +++ b/packages/api/src/features/resume/event-router.ts @@ -1,3 +1,4 @@ +import { eventIterator } from "@orpc/server"; import z from "zod"; import { protectedProcedure } from "../../context"; import { subscribeResumeUpdated } from "./events"; @@ -16,6 +17,17 @@ export const updatesRouter = { successDescription: "A stream of resume update invalidation events.", }) .input(z.object({ id: z.string().describe("The unique identifier of the resume.") })) + .output( + eventIterator( + z.object({ + type: z.literal("resume.updated"), + resumeId: z.string(), + userId: z.string(), + updatedAt: z.string(), + mutation: z.enum(["sync", "create", "update", "patch", "lock", "password", "delete"]), + }), + ), + ) .handler(async function* ({ context, input, signal }) { const resume = await resumeService.getById({ id: input.id, userId: context.user.id }); diff --git a/packages/api/src/features/resume/imports.test.ts b/packages/api/src/features/resume/imports.test.ts new file mode 100644 index 000000000..1ef77a6f0 --- /dev/null +++ b/packages/api/src/features/resume/imports.test.ts @@ -0,0 +1,31 @@ +import { expect, it, vi } from "vitest"; +import { call } from "@orpc/server"; +const create = vi.hoisted(() => vi.fn()); +vi.mock("./service", () => ({ resumeService: { create } })); +vi.mock("@reactive-resume/auth/config", () => ({ + auth: { api: { getSession: vi.fn().mockResolvedValue({ user: { id: "owner" } }) } }, + verifyOAuthToken: vi.fn(), +})); +const { importResumeFile } = await import("./imports"); + +it("imports JSON Resume into the authenticated account and rejects invalid files before writing", async () => { + const context = { locale: "en-US" as const, reqHeaders: new Headers() }; + const id = await call( + importResumeFile, + { file: new File([JSON.stringify({ basics: { name: "Ada Lovelace" } })], "resume.json"), format: "JSON_RESUME" }, + { context }, + ); + expect(create).toHaveBeenCalledWith( + expect.objectContaining({ + id, + userId: "owner", + name: "Ada Lovelace", + data: expect.objectContaining({ basics: expect.objectContaining({ name: "Ada Lovelace" }) }), + }), + ); + create.mockClear(); + await expect( + call(importResumeFile, { file: new File(["bad json"], "resume.json"), format: "JSON_RESUME" }, { context }), + ).rejects.toMatchObject({ code: "BAD_REQUEST" }); + expect(create).not.toHaveBeenCalled(); +}); diff --git a/packages/api/src/features/resume/imports.ts b/packages/api/src/features/resume/imports.ts new file mode 100644 index 000000000..2bdcc731e --- /dev/null +++ b/packages/api/src/features/resume/imports.ts @@ -0,0 +1,67 @@ +import { call, ORPCError } from "@orpc/server"; +import z from "zod"; +import { protectedProcedure } from "../../context"; +import { storageUploadRateLimit } from "../../middleware/rate-limit"; +import { crudRouter } from "./crud"; + +export const importResumeFile = protectedProcedure + .route({ + method: "POST", + path: "/resumes/file-imports", + tags: ["Resumes"], + operationId: "importResumeFile", + summary: "Import a PDF, resume JSON or LinkedIn archive", + description: + "Multipart form with file and format fields. PDF imports read the text layer without AI. For AI-assisted PDF and Word imports, use the AI parsing endpoints then create a resume import.", + }) + .input( + z.object({ + file: z.file().max(10 * 1024 * 1024), + format: z.enum(["REACTIVE_RESUME", "REACTIVE_RESUME_V4", "JSON_RESUME", "LINKEDIN", "PDF"]), + }), + ) + .output(z.string().describe("The imported resume ID.")) + .use(storageUploadRateLimit) + .handler(async ({ input, context, signal }) => { + let data; + try { + if (input.format === "PDF") { + const [{ extractPdf }, { documentToLines }, { buildExtractedDocument }, { parseResumeText }] = + await Promise.all([ + import("./pdf-analysis"), + import("@reactive-resume/import/pdf-lines"), + import("@reactive-resume/resume/ats-pdf"), + import("@reactive-resume/import/plain-text"), + ]); + const raw = await extractPdf(input.file, signal, 0); + if (raw.truncated) throw new Error("The PDF has too many pages to import completely."); + const lines = documentToLines(buildExtractedDocument(raw)); + if (lines.length === 0) throw new Error("The PDF has no readable text layer."); + data = parseResumeText(lines.join("\n")); + } else if (input.format === "LINKEDIN") { + const { parseLinkedInExport } = await import("@reactive-resume/import/linkedin"); + data = parseLinkedInExport(new Uint8Array(await input.file.arrayBuffer())); + } else { + const text = await input.file.text(); + if (input.format === "JSON_RESUME") { + const { parseJSONResume } = await import("@reactive-resume/import/json-resume"); + data = parseJSONResume(text); + } else if (input.format === "REACTIVE_RESUME_V4") { + const { parseReactiveResumeV4JSON } = await import("@reactive-resume/import/reactive-resume-v4-json"); + data = parseReactiveResumeV4JSON(text); + } else { + const { parseReactiveResumeJSON } = await import("@reactive-resume/import/reactive-resume-json"); + data = parseReactiveResumeJSON(text); + } + } + const { convertLegacyStylesheet, needsLegacyStyleConversion } = + await import("@reactive-resume/pdf/semantic-legacy"); + if (needsLegacyStyleConversion(data.metadata)) data.metadata.stylesheet = convertLegacyStylesheet(data); + } catch (error) { + throw new ORPCError("BAD_REQUEST", { + message: "The file is not a valid resume in the selected format.", + cause: error, + }); + } + return call(crudRouter.import, { data }, { context }); + }); diff --git a/packages/api/src/features/resume/pagination.test.ts b/packages/api/src/features/resume/pagination.test.ts new file mode 100644 index 000000000..b7fffcb7f --- /dev/null +++ b/packages/api/src/features/resume/pagination.test.ts @@ -0,0 +1,32 @@ +import { expect, it, vi } from "vitest"; +import { call } from "@orpc/server"; +const list = vi.hoisted(() => vi.fn()); +vi.mock("./service", () => ({ resumeService: { list } })); +vi.mock("@reactive-resume/auth/config", () => ({ + auth: { api: { getSession: vi.fn().mockResolvedValue({ user: { id: "owner" } }) } }, + verifyOAuthToken: vi.fn(), +})); +const { crudRouter } = await import("./crud"); + +it("paginates the owner's filtered list without changing legacy array responses", async () => { + const rows = ["a", "b", "c"].map((id) => ({ + id, + name: id, + slug: id, + tags: ["engineering"], + isPublic: false, + isLocked: false, + showDownloadButtons: false, + createdAt: new Date(), + updatedAt: new Date(), + })); + list.mockResolvedValue(rows); + const resHeaders = new Headers(); + const context = { locale: "en-US" as const, reqHeaders: new Headers(), resHeaders }; + const result = await call(crudRouter.list, { tags: ["engineering"], limit: 1, offset: 1 }, { context }); + expect(result.map((row) => row.id)).toEqual(["b"]); + expect(resHeaders.get("X-Total-Count")).toBe("3"); + expect(list).toHaveBeenCalledWith({ userId: "owner", tags: ["engineering"], sort: "lastUpdatedAt" }); + expect(await call(crudRouter.list, {}, { context })).toEqual(rows); + await expect(call(crudRouter.list, { limit: 101 }, { context })).rejects.toMatchObject({ code: "BAD_REQUEST" }); +}); diff --git a/packages/api/src/features/resume/pdf-analysis.test.ts b/packages/api/src/features/resume/pdf-analysis.test.ts new file mode 100644 index 000000000..ec5e44773 --- /dev/null +++ b/packages/api/src/features/resume/pdf-analysis.test.ts @@ -0,0 +1,39 @@ +import { expect, it, vi } from "vitest"; +import { call } from "@orpc/server"; +vi.mock("@reactive-resume/auth/config", () => ({ + auth: { api: { getSession: vi.fn().mockResolvedValue(null) } }, + verifyOAuthToken: vi.fn(), +})); +const { checkPdf } = await import("./checks"); + +// A complete, tiny one-page PDF; no network fonts, renderer, database or mocked PDF reader. +function pdfFile() { + const stream = "BT /F1 12 Tf 50 700 Td (Ada Lovelace analytical engines) Tj ET"; + const objects = [ + "<< /Type /Catalog /Pages 2 0 R >>", + "<< /Type /Pages /Kids [3 0 R] /Count 1 >>", + "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>", + "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", + `<< /Length ${stream.length} >>\nstream\n${stream}\nendstream`, + ]; + let text = "%PDF-1.4\n"; + const offsets = objects.map((object, index) => { + const offset = text.length; + text += `${index + 1} 0 obj\n${object}\nendobj\n`; + return offset; + }); + const xref = text.length; + text += `xref\n0 6\n0000000000 65535 f \n${offsets.map((offset) => `${String(offset).padStart(10, "0")} 00000 n \n`).join("")}trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF`; + return new File([text], "resume.pdf", { type: "application/pdf" }); +} + +it("checks an anonymous PDF upload with the real reader and rejects non-PDF bytes", async () => { + const context = { locale: "en-US" as const, reqHeaders: new Headers(), trustedClient: "127.0.0.1" }; + const report = await call(checkPdf, { file: pdfFile() }, { context }); + expect(report.document).toMatchObject({ pageCount: 1, truncated: false, wordCount: 4 }); + expect(report.file.magicBytesOk).toBe(true); + expect(report.fullText).toContain("Ada Lovelace analytical engines"); + await expect(call(checkPdf, { file: new File(["not a PDF"], "resume.pdf") }, { context })).rejects.toMatchObject({ + code: "BAD_REQUEST", + }); +}); diff --git a/packages/api/src/features/resume/pdf-analysis.ts b/packages/api/src/features/resume/pdf-analysis.ts new file mode 100644 index 000000000..72531428c --- /dev/null +++ b/packages/api/src/features/resume/pdf-analysis.ts @@ -0,0 +1,35 @@ +import { ORPCError } from "@orpc/server"; +import { harvestPdfDocument } from "@reactive-resume/resume/ats-pdf"; + +/** Server adapter for the same bounded extraction used by the browser's checker and importer. */ +export async function extractPdf(file: File, signal?: AbortSignal, operatorBudgetMs = 30_000) { + const data = new Uint8Array(await file.arrayBuffer()); + if (file.size > 25_000_000 || ![0x25, 0x50, 0x44, 0x46].every((byte, index) => data[index] === byte)) { + throw new ORPCError("BAD_REQUEST", { message: "Provide a PDF no larger than 25 MB." }); + } + const { getDocument } = await import("pdfjs-dist/legacy/build/pdf.mjs"); + const task = getDocument({ data, fontExtraProperties: true, useSystemFonts: false }); + const timeout = AbortSignal.timeout(45_000); + const aborted = signal ? AbortSignal.any([signal, timeout]) : timeout; + const cancel = () => { + void task.destroy().catch(() => {}); + }; + aborted.addEventListener("abort", cancel, { once: true }); + try { + aborted.throwIfAborted(); + const document = await task.promise; + return await harvestPdfDocument(document, { + file: { name: file.name, sizeBytes: file.size, magicBytesOk: true }, + signal: aborted, + operatorBudgetMs, + }); + } catch (cause) { + throw new ORPCError("BAD_REQUEST", { + message: "The PDF could not be read. Use an unencrypted PDF with a readable text layer.", + cause, + }); + } finally { + aborted.removeEventListener("abort", cancel); + await task.destroy().catch(() => {}); + } +} diff --git a/packages/api/src/features/resume/sharing.ts b/packages/api/src/features/resume/sharing.ts index f70123a85..240d50733 100644 --- a/packages/api/src/features/resume/sharing.ts +++ b/packages/api/src/features/resume/sharing.ts @@ -79,7 +79,7 @@ export const sharingRouter = { z.object({ username: z.string().min(1).describe("The username of the resume owner."), slug: z.string().min(1).describe("The slug of the resume."), - password: z.string().min(1).describe("The password to verify."), + password: z.string().min(1).max(64).describe("The password to verify."), }), ) .use(resumePasswordRateLimit) diff --git a/packages/api/src/features/resume/tags.ts b/packages/api/src/features/resume/tags.ts index 19ac62492..0d97fcf96 100644 --- a/packages/api/src/features/resume/tags.ts +++ b/packages/api/src/features/resume/tags.ts @@ -1,5 +1,6 @@ import z from "zod"; import { protectedProcedure } from "../../context"; +import { paginate, paginationShape } from "../../pagination"; import { resumeService } from "./service"; export const tagsRouter = { @@ -15,5 +16,8 @@ export const tagsRouter = { successDescription: "A sorted array of unique tag strings.", }) .output(z.array(z.string())) - .handler(({ context }) => resumeService.tags.list({ userId: context.user.id })), + .input(z.object(paginationShape).default({})) + .handler(async ({ context, input }) => + paginate(await resumeService.tags.list({ userId: context.user.id }), input, context.resHeaders), + ), }; diff --git a/packages/api/src/features/resume/versions.ts b/packages/api/src/features/resume/versions.ts index 0006652a9..ad0667318 100644 --- a/packages/api/src/features/resume/versions.ts +++ b/packages/api/src/features/resume/versions.ts @@ -1,6 +1,7 @@ import { protectedProcedure } from "../../context"; import { resumeDto } from "../../dto/resume"; import { resumeMutationRateLimit } from "../../middleware/rate-limit"; +import { paginate } from "../../pagination"; import { resumeService } from "./service"; export const versionsRouter = { @@ -17,8 +18,12 @@ export const versionsRouter = { }) .input(resumeDto.listVersions.input) .output(resumeDto.listVersions.output) - .handler(({ context, input }) => - resumeService.versions.list({ resumeId: input.resumeId, userId: context.user.id }), + .handler(async ({ context, input }) => + paginate( + await resumeService.versions.list({ resumeId: input.resumeId, userId: context.user.id }), + input, + context.resHeaders, + ), ), getVersion: protectedProcedure diff --git a/packages/api/src/features/statistics/router.ts b/packages/api/src/features/statistics/router.ts index 9c5d90efc..bcade7502 100644 --- a/packages/api/src/features/statistics/router.ts +++ b/packages/api/src/features/statistics/router.ts @@ -14,6 +14,7 @@ const githubRouter = { "Returns the number of GitHub stars for the Reactive Resume repository. The count is cached for up to 6 hours and falls back to a last-known value if the GitHub API is unavailable. No authentication required.", successDescription: "The number of GitHub stars for the Reactive Resume repository.", }) + .input(z.object({}).optional()) .output(z.number().describe("The number of GitHub stars.")) .handler(() => statisticsService.github.getStarCount()), }; @@ -27,6 +28,7 @@ export const statisticsRouter = { operationId: "getStatisticsTotals", summary: "Get user and resume totals with their cache timestamp", }) + .input(z.object({}).optional()) .output( z.object({ users: z.number(), diff --git a/packages/api/src/features/statistics/service.ts b/packages/api/src/features/statistics/service.ts index a5b1e0c8a..32d784b63 100644 --- a/packages/api/src/features/statistics/service.ts +++ b/packages/api/src/features/statistics/service.ts @@ -1,4 +1,5 @@ import { count } from "drizzle-orm"; +import z from "zod"; import { db } from "@reactive-resume/db/client"; import * as schema from "@reactive-resume/db/schema"; @@ -62,9 +63,8 @@ const fetchGitHubStarsOnce = async (): Promise => { }); if (!response.ok) return null; - const data = (await response.json()) as { stargazers_count?: unknown }; - const stars = Number(data.stargazers_count); - return Number.isFinite(stars) && stars > 0 ? stars : null; + const data = z.object({ stargazers_count: z.number().int().nonnegative() }).safeParse(await response.json()); + return data.success ? data.data.stargazers_count : null; } catch { return null; } diff --git a/packages/api/src/features/storage/router.ts b/packages/api/src/features/storage/router.ts index ece346467..be738774c 100644 --- a/packages/api/src/features/storage/router.ts +++ b/packages/api/src/features/storage/router.ts @@ -20,6 +20,12 @@ function isUnsafeStorageKey(key: string): boolean { return key.split("/").some((segment) => segment === "." || segment === ".."); } +export const uploadFileOutputSchema = z.object({ + url: z.string().describe("The download URL. Profile images are public; other files require owner authentication."), + path: z.string().describe("The storage path of the uploaded file."), + contentType: z.string().describe("The MIME type of the uploaded file."), +}); + export const storageRouter = { uploadFile: protectedProcedure .route({ @@ -32,13 +38,7 @@ export const storageRouter = { }) .input(fileSchema) .use(storageUploadRateLimit) - .output( - z.object({ - url: z.string().describe("The public URL to access the uploaded file."), - path: z.string().describe("The storage path of the uploaded file."), - contentType: z.string().describe("The MIME type of the uploaded file."), - }), - ) + .output(uploadFileOutputSchema) .handler(async ({ context, input: file }) => { const originalMimeType = file.type; const isImage = isImageFile(originalMimeType); diff --git a/packages/api/src/features/web-access/router.ts b/packages/api/src/features/web-access/router.ts index 5d1cbfd5e..02896f007 100644 --- a/packages/api/src/features/web-access/router.ts +++ b/packages/api/src/features/web-access/router.ts @@ -1,4 +1,5 @@ import { ORPCError } from "@orpc/client"; +import z from "zod"; import { protectedProcedure } from "../../context"; import { webAccessDto } from "../../dto/web-access"; import { WebAccessError } from "./contracts"; @@ -20,6 +21,7 @@ export const webAccessRouter = { operationId: "getWebAccessStatus", summary: "Get search and reading availability", }) + .input(z.object({}).optional()) .output(webAccessDto.status.output) .handler(({ context }) => webAccessService.status(context.user.id)), save: protectedProcedure @@ -43,6 +45,7 @@ export const webAccessRouter = { summary: "Remove the personal web connection", }) .errors(errors) + .input(z.object({}).optional()) .output(webAccessDto.delete.output) .handler(({ context }) => webAccessService.delete(context.user.id)), test: protectedProcedure @@ -57,6 +60,7 @@ export const webAccessRouter = { PRECONDITION_FAILED: { message: "No external web connection is configured.", status: 412 }, RATE_LIMIT_EXCEEDED: { message: "Too many web requests. Try again later.", status: 429 }, }) + .input(z.object({}).optional()) .output(webAccessDto.test.output) .handler(async ({ context, signal }) => { const connection = await webAccessService.resolve(context.user.id); diff --git a/packages/api/src/middleware/rate-limit/index.ts b/packages/api/src/middleware/rate-limit/index.ts index e28814b53..08ff73ef3 100644 --- a/packages/api/src/middleware/rate-limit/index.ts +++ b/packages/api/src/middleware/rate-limit/index.ts @@ -101,3 +101,9 @@ export const resumeMutationRateLimit = createRatelimitMiddleware `resume-mutation:${getUserKey(context)}:${getInputKeyPart(input)}`, }); + +const pdfAnalysisLimiter = createRateLimiter("pdfAnalysisLimiter", rateLimitConfig.orpc.pdfExport); +export const pdfAnalysisRateLimit = createRatelimitMiddleware({ + limiter: productionLimiter(pdfAnalysisLimiter), + key: ({ context }) => `pdf-analysis:${getClientKey(context.trustedClient)}`, +}); diff --git a/packages/api/src/pagination.ts b/packages/api/src/pagination.ts new file mode 100644 index 000000000..1c37fcba6 --- /dev/null +++ b/packages/api/src/pagination.ts @@ -0,0 +1,34 @@ +import z from "zod"; + +// Opt-in pagination keeps existing array responses and clients that load the full library compatible. +export const paginationShape = { + limit: z + .number() + .int() + .min(1) + .max(100) + .optional() + .describe("Maximum results (1–100). Omit both pagination fields for the legacy complete list."), + offset: z + .number() + .int() + .min(0) + .max(Number.MAX_SAFE_INTEGER) + .optional() + .describe("Zero-based offset. Defaults to 0; limit defaults to 20 when only offset is given."), +}; + +export function paginate( + items: T[], + input: { limit?: number | undefined; offset?: number | undefined }, + headers?: Headers, +): T[] { + headers?.set("X-Total-Count", String(items.length)); + if (input.limit === undefined && input.offset === undefined) return items; + const limit = input.limit ?? 20; + const offset = input.offset ?? 0; + headers?.set("X-Limit", String(limit)); + headers?.set("X-Offset", String(offset)); + // ponytail: slice the existing per-user library in memory; move pagination into SQL if large libraries dominate memory. + return items.slice(offset, offset + limit); +} diff --git a/packages/api/src/routers/rest.ts b/packages/api/src/routers/rest.ts new file mode 100644 index 000000000..5ed7a10c8 --- /dev/null +++ b/packages/api/src/routers/rest.ts @@ -0,0 +1,198 @@ +import { call } from "@orpc/server"; +import z from "zod"; +import { protectedProcedure } from "../context"; +import { documentExports } from "../features/documents/exports"; +import { checkResume, checkPdf, matchResume } from "../features/resume/checks"; +import { importResumeFile } from "../features/resume/imports"; +import { uploadFileOutputSchema } from "../features/storage/router"; +import router from "./index"; + +// Add resource-oriented aliases without changing the routes existing clients use. +export const restAliases = { + documentExports, + checkResume, + checkPdf, + matchResume, + importResumeFile, + updateCoverLetter: router.coverLetters.update.route({ method: "PATCH", operationId: "patchCoverLetter" }), + updateApplication: router.applications.update.route({ method: "PATCH", operationId: "patchApplication" }), + updateInterview: router.applications.updateInterview.route({ + method: "PATCH", + operationId: "patchApplicationInterview", + }), + updateTimelineEntry: router.applications.updateTimelineEntry.route({ + method: "PATCH", + operationId: "patchApplicationTimelineEntry", + }), + updateResume: router.resume.update.route({ + method: "PATCH", + path: "/resumes/{id}/metadata", + operationId: "patchResumeMetadata", + }), + resumeCopy: router.resume.duplicate.route({ path: "/resumes/{id}/copies", operationId: "createResumeCopy" }), + resumeImport: router.resume.import.route({ path: "/resumes/imports", operationId: "createResumeImport" }), + resumeLock: router.resume.setLocked.route({ + method: "PUT", + path: "/resumes/{id}/lock", + operationId: "putResumeLock", + }), + resumeRestoration: router.resume.restoreVersion.route({ + path: "/resumes/{resumeId}/versions/{versionId}/restorations", + operationId: "createResumeRestoration", + }), + resumePasswordVerification: router.resume.verifyPassword.route({ + path: "/resumes/{username}/{slug}/password-verifications", + operationId: "createResumePasswordVerification", + }), + resumeSlugAvailability: router.resume.checkSlug.route({ + path: "/resumes/{resumeId}/slug-availability", + operationId: "getResumeSlugAvailability", + }), + resumeDownload: router.resume.statistics.recordDownload.route({ + path: "/resumes/{username}/{slug}/statistics/downloads", + operationId: "createResumeDownload", + }), + letterCopy: router.coverLetters.duplicate.route({ + path: "/cover-letters/{id}/copies", + operationId: "createCoverLetterCopy", + }), + letterImport: router.coverLetters.import.route({ + path: "/cover-letters/imports", + operationId: "createCoverLetterImport", + }), + letterStyle: router.coverLetters.refreshStyle.route({ + path: "/cover-letters/{id}/style-refreshes", + operationId: "createCoverLetterStyleRefresh", + }), + letterDraft: router.coverLetters.draft.route({ + path: "/cover-letters/{id}/drafts", + operationId: "createCoverLetterDraft", + }), + letterRestoration: router.coverLetters.restoreVersion.route({ + path: "/cover-letters/{id}/versions/{versionId}/restorations", + operationId: "createCoverLetterRestoration", + }), + documentName: router.documents.rename.route({ + method: "PATCH", + path: "/documents/{type}/{id}/name", + operationId: "patchDocumentName", + }), + documentTags: router.documents.setTags.route({ + method: "PUT", + path: "/documents/{type}/{id}/tags", + operationId: "putDocumentTags", + }), + documentLock: router.documents.setLocked.route({ + method: "PUT", + path: "/documents/{type}/{id}/lock", + operationId: "putDocumentLock", + }), + documentApplication: router.documents.linkApplication.route({ + method: "PUT", + path: "/documents/{type}/{id}/application", + operationId: "putDocumentApplication", + }), + documentTrash: router.documents.trash.route({ + method: "PUT", + path: "/documents/{type}/{id}/trash", + operationId: "putDocumentTrash", + }), + documentRestore: router.documents.restore.route({ + method: "DELETE", + path: "/documents/{type}/{id}/trash", + operationId: "deleteDocumentTrash", + }), + documentPurge: router.documents.purge.route({ + method: "DELETE", + path: "/documents/{type}/{id}", + operationId: "deleteDocumentPermanently", + }), + documentCopy: router.documents.copyForJob.route({ path: "/documents/copies", operationId: "createDocumentCopy" }), + postingSearch: router.applications.ai.searchPostings.route({ + path: "/applications/ai/posting-searches", + operationId: "createPostingSearch", + }), + postingParse: router.applications.ai.parsePosting.route({ + path: "/applications/ai/posting-parses", + operationId: "createPostingParse", + }), + applicationAutofill: router.applications.ai.autofill.route({ + path: "/applications/ai/autofills", + operationId: "createApplicationAutofill", + }), + applicationMatch: router.applications.ai.matchScore.route({ + path: "/applications/{id}/ai/match-scores", + operationId: "createApplicationMatchScore", + }), + applicationMessage: router.applications.ai.draftMessage.route({ + path: "/applications/{id}/ai/message-drafts", + operationId: "createApplicationMessageDraft", + }), + applicationResume: router.applications.ai.tailorResume.route({ + path: "/applications/{id}/ai/tailored-resumes", + operationId: "createTailoredResume", + }), + applicationImport: router.applications.import.route({ + path: "/applications/imports", + operationId: "createApplicationImport", + }), + applicationBulkUpdate: router.applications.bulkUpdate.route({ + method: "PATCH", + path: "/applications", + operationId: "patchApplications", + }), + applicationBulkDelete: router.applications.bulkDelete.route({ + method: "DELETE", + path: "/applications", + operationId: "deleteApplications", + }), + providerTest: router.aiProviders.test.route({ + path: "/ai-providers/{id}/tests", + operationId: "createAiProviderTest", + }), + webAccessTest: router.webAccess.test.route({ + path: "/integrations/web-access/tests", + operationId: "createWebAccessTest", + }), + message: router.agent.messages.send.route({ + path: "/agent/threads/{threadId}/messages", + operationId: "createAgentMessage", + }), + messageStop: router.agent.messages.stop.route({ + method: "DELETE", + path: "/agent/threads/{threadId}/run", + operationId: "deleteAgentRun", + }), + messageStream: router.agent.messages.resume.route({ + path: "/agent/threads/{threadId}/stream", + operationId: "getAgentStream", + }), + messageEdits: router.agent.messages.setEditStatus.route({ + method: "PATCH", + path: "/agent/threads/{threadId}/messages/{messageId}/edits", + operationId: "patchAgentMessageEdits", + }), + pdfParsing: router.ai.parsePdf.route({ path: "/ai/pdf-parses", operationId: "createPdfParse" }), + docxParsing: router.ai.parseDocx.route({ path: "/ai/docx-parses", operationId: "createDocxParse" }), + atsReview: router.ai.atsReview.route({ path: "/ai/ats-reviews", operationId: "createAtsReview" }), + improvement: router.ai.improve.route({ path: "/ai/improvements", operationId: "createImprovement" }), + fileUpload: protectedProcedure + .route({ + method: "POST", + path: "/files", + tags: ["Files"], + operationId: "createFile", + summary: "Upload a file", + description: + "Send a multipart form with a file field (maximum 10 MiB). Images become public profile pictures; other files remain owner-only. Requires authentication.", + }) + .input(z.object({ file: z.file().max(10 * 1024 * 1024) })) + .output(uploadFileOutputSchema) + .handler(({ input, context }) => call(router.storage.uploadFile, input.file, { context })), + fileDelete: router.storage.deleteFile.route({ + method: "DELETE", + path: "/files", + tags: ["Files"], + operationId: "deleteStoredFile", + }), +}; diff --git a/packages/auth/src/config.test.ts b/packages/auth/src/config.test.ts index 92a552c19..287cb311b 100644 --- a/packages/auth/src/config.test.ts +++ b/packages/auth/src/config.test.ts @@ -21,3 +21,12 @@ describe("session freshness", () => { expect(auth.options.session?.freshAge).toBe(0); }); }); + +it("publishes the configured native authentication HTTP contract", async () => { + const response = await auth.handler(new Request(`${env.APP_URL}/api/auth/open-api/generate-schema`)); + expect(response.status).toBe(200); + const spec = await response.json(); + expect(spec.paths).toHaveProperty("/sign-in/email"); + expect(spec.paths).toHaveProperty("/get-session"); + expect(spec.paths).toHaveProperty("/api-key/create"); +}); diff --git a/packages/auth/src/config.ts b/packages/auth/src/config.ts index b1619bf21..0bf3cf90e 100644 --- a/packages/auth/src/config.ts +++ b/packages/auth/src/config.ts @@ -9,7 +9,7 @@ import { compare, hash } from "bcryptjs"; import { APIError, betterAuth } from "better-auth"; import { createAuthMiddleware } from "better-auth/api"; import { verifyBearerToken } from "better-auth/oauth2"; -import { admin, jwt } from "better-auth/plugins"; +import { admin, jwt, openAPI } from "better-auth/plugins"; import { genericOAuth } from "better-auth/plugins/generic-oauth"; import { twoFactor } from "better-auth/plugins/two-factor"; import { username } from "better-auth/plugins/username"; @@ -303,6 +303,7 @@ const getAuthConfig = () => { }, plugins: [ + openAPI({ disableDefaultReference: true }), jwt(), admin(), passkey(), diff --git a/packages/docx/package.json b/packages/docx/package.json index 310461965..abf00a842 100644 --- a/packages/docx/package.json +++ b/packages/docx/package.json @@ -16,7 +16,8 @@ "dependencies": { "@reactive-resume/schema": "workspace:*", "@reactive-resume/utils": "workspace:*", - "docx": "^9.8.1" + "docx": "^9.8.1", + "node-html-parser": "^9.0.4" }, "devDependencies": { "@reactive-resume/config": "workspace:*", diff --git a/packages/docx/src/html-to-docx.ts b/packages/docx/src/html-to-docx.ts index b47970833..573a9c4fa 100644 --- a/packages/docx/src/html-to-docx.ts +++ b/packages/docx/src/html-to-docx.ts @@ -1,5 +1,7 @@ import type { IShadingAttributesProperties } from "docx"; +import type { HTMLElement, Node } from "node-html-parser"; import { ExternalHyperlink, HeadingLevel, Paragraph, TextRun } from "docx"; +import { parse, NodeType } from "node-html-parser"; import { isDarkColor, parseColorString } from "@reactive-resume/utils/color"; import { toSafeDocxLink } from "./link-utils"; @@ -24,11 +26,11 @@ interface InlineStyle { type InlineChild = TextRun | ExternalHyperlink; const preservesWhitespace = (node: Node) => { - let ancestor = node.parentElement; + let ancestor = node.parentNode; while (ancestor) { if (/^(P|H[1-6])$/.test(ancestor.tagName) && ancestor.getAttribute("data-resume-whitespace") === "preserve") return true; - ancestor = ancestor.parentElement; + ancestor = ancestor.parentNode; } return false; }; @@ -52,6 +54,18 @@ function toDocxColorValue(value: string) { return [rgba.r, rgba.g, rgba.b].map((channel) => channel.toString(16).padStart(2, "0").toUpperCase()).join(""); } +function styleProperty(element: HTMLElement | undefined, property: string): string | undefined { + return element + ?.getAttribute("style") + ?.split(";") + .reverse() + .map((declaration) => { + const colon = declaration.indexOf(":"); + return [declaration.slice(0, colon).trim().toLowerCase(), declaration.slice(colon + 1).trim()]; + }) + .find(([name]) => name === property)?.[1]; +} + function mergeStyle(parent: InlineStyle, tag: string, element?: HTMLElement): InlineStyle { const next = { ...parent }; @@ -75,7 +89,7 @@ function mergeStyle(parent: InlineStyle, tag: string, element?: HTMLElement): In next.font = "Courier New"; break; case "MARK": { - const bgColor = (element as HTMLElement | undefined)?.style.backgroundColor; + const bgColor = styleProperty(element, "background-color"); const fill = bgColor ? toDocxColorValue(bgColor) : null; next.shading = { fill: fill ?? "FFFF00" }; if (bgColor && isDarkColor(bgColor)) next.color = "FFFFFF"; @@ -83,7 +97,7 @@ function mergeStyle(parent: InlineStyle, tag: string, element?: HTMLElement): In } } - const colorValue = (element as HTMLElement | undefined)?.style.color; + const colorValue = styleProperty(element, "color"); if (colorValue) { const color = toDocxColorValue(colorValue); if (color) next.color = color; @@ -96,7 +110,7 @@ function collectInlineChildren(node: Node, style: InlineStyle): InlineChild[] { const children: InlineChild[] = []; for (const child of node.childNodes) { - if (child.nodeType === Node.TEXT_NODE) { + if (child.nodeType === NodeType.TEXT_NODE) { const text = (child.textContent ?? "").replace(/\t/g, preservesWhitespace(child) ? " " : "\t"); if (text) { children.push(new TextRun({ text, ...style })); @@ -104,7 +118,7 @@ function collectInlineChildren(node: Node, style: InlineStyle): InlineChild[] { continue; } - if (child.nodeType !== Node.ELEMENT_NODE) continue; + if (child.nodeType !== NodeType.ELEMENT_NODE) continue; const el = child as HTMLElement; const tag = el.tagName; @@ -184,7 +198,7 @@ function processBlockElement( if (hasNestedBlocks) { for (const liChild of li.childNodes) { - if (liChild.nodeType === Node.TEXT_NODE) { + if (liChild.nodeType === NodeType.TEXT_NODE) { const text = (liChild.textContent ?? "").trim(); if (text) { paragraphs.push( @@ -195,7 +209,7 @@ function processBlockElement( }), ); } - } else if (liChild.nodeType === Node.ELEMENT_NODE) { + } else if (liChild.nodeType === NodeType.ELEMENT_NODE) { processBlockElement(liChild as HTMLElement, mergedStyle, paragraphs, level, quoteIndent); } } @@ -217,16 +231,16 @@ function processBlockElement( if (tag === "BLOCKQUOTE") { const quoteStyle = { ...mergedStyle, italics: true }; - const inline = el.ownerDocument.createElement("p"); + const inline = parse("

").firstChild as HTMLElement; const flushInline = () => { - if (!inline.hasChildNodes()) return; + if (inline.childNodes.length === 0) return; processBlockElement(inline, quoteStyle, paragraphs, listLevel, quoteIndent + 720); - inline.replaceChildren(); + inline.set_content([]); }; // Keep each semantic paragraph's own offset and preserve adjacent inline // content as one paragraph. Nested quotes add their existing 720-twip inset. for (const child of el.childNodes) { - if (child.nodeType === Node.ELEMENT_NODE) { + if (child.nodeType === NodeType.ELEMENT_NODE) { const element = child as HTMLElement; if (HEADING_MAP[element.tagName] || /^(P|DIV|UL|OL|BLOCKQUOTE|PRE|HR)$/.test(element.tagName)) { flushInline(); @@ -234,8 +248,9 @@ function processBlockElement( continue; } } - if (child.nodeType === Node.TEXT_NODE && !child.textContent?.trim() && !inline.hasChildNodes()) continue; - inline.appendChild(child.cloneNode(true)); + if (child.nodeType === NodeType.TEXT_NODE && !child.textContent?.trim() && inline.childNodes.length === 0) + continue; + inline.appendChild(child.clone()); } flushInline(); return; @@ -281,7 +296,7 @@ function processBlockElement( /** * Converts an HTML string (from TipTap rich text editor) into an array of docx Paragraphs. - * Uses the browser's DOMParser to parse HTML, then walks the DOM tree to produce + * Parses HTML without browser globals, then walks the tree to produce * structured docx content with proper formatting (bold, italic, lists, links, etc.). * * @param html - The HTML string to convert @@ -297,14 +312,13 @@ export function htmlToParagraphs(html: string, styleConfig?: HtmlStyleConfig): P if (styleConfig?.size) baseStyle.size = styleConfig.size; if (styleConfig?.color) baseStyle.color = styleConfig.color; - const parser = new DOMParser(); - const doc = parser.parseFromString(html, "text/html"); + const doc = parse(html); // Script and style text is code, not content: drop it before any walk below prints it. - for (const element of doc.body.querySelectorAll("script, style")) element.remove(); + for (const element of doc.querySelectorAll("script, style")) element.remove(); const paragraphs: Paragraph[] = []; - for (const child of doc.body.childNodes) { - if (child.nodeType === Node.TEXT_NODE) { + for (const child of doc.childNodes) { + if (child.nodeType === NodeType.TEXT_NODE) { const text = (child.textContent ?? "").trim(); if (text) { paragraphs.push(new Paragraph({ children: [new TextRun({ text, ...baseStyle })] })); @@ -312,7 +326,7 @@ export function htmlToParagraphs(html: string, styleConfig?: HtmlStyleConfig): P continue; } - if (child.nodeType === Node.ELEMENT_NODE) { + if (child.nodeType === NodeType.ELEMENT_NODE) { processBlockElement(child as HTMLElement, baseStyle, paragraphs); } } diff --git a/packages/import/package.json b/packages/import/package.json index cad689baf..7221c8cd1 100644 --- a/packages/import/package.json +++ b/packages/import/package.json @@ -6,6 +6,7 @@ "exports": { "./json-resume": "./src/json-resume.tsx", "./linkedin": "./src/linkedin.ts", + "./pdf-lines": "./src/pdf-lines.ts", "./plain-text": "./src/plain-text.ts", "./reactive-resume-json": "./src/reactive-resume-json.tsx", "./reactive-resume-v4-json": "./src/reactive-resume-v4-json.tsx" diff --git a/packages/import/src/pdf-lines.ts b/packages/import/src/pdf-lines.ts new file mode 100644 index 000000000..722397cb8 --- /dev/null +++ b/packages/import/src/pdf-lines.ts @@ -0,0 +1,76 @@ +import type { ExtractedDocument } from "@reactive-resume/resume/ats-pdf"; + +/** + * Turns a PDF into the plain lines `parseResumeText` reads. + * + * The extraction itself is the ATS checker's — same worker configuration, same size and password + * guards, same font-relative line clustering and column-gutter detection. Only the joining differs: + * the ATS text is prose, while the importer splits header rows on runs of two or more spaces, so a + * tabbed "Company Role Location" has to survive as three fields rather than one phrase. + */ + +/** Horizontal gap, relative to font size, above which two spans are separate header fields. */ +const FIELD_GAP_RATIO = 1.5; +/** Narrower gaps are still a word break; matches the ratio the ATS extractor joins prose with. */ +const SPACE_GAP_RATIO = 0.25; + +/** The evidence the ATS layout rules require before calling a page two-column. */ +const GUTTER_COVERAGE = 0.85; +const GUTTER_SPLIT_RATIO = 0.25; + +type PageGeometry = ExtractedDocument["pages"][number]; +type TextSpan = PageGeometry["lines"][number]["spans"][number]; + +function joinSpans(spans: readonly TextSpan[]): string { + let text = ""; + let previous: TextSpan | null = null; + + for (const span of spans) { + if (previous) { + const gap = span.x - (previous.x + previous.width); + const fontSize = Math.max(previous.fontSize, span.fontSize, 1); + + if (gap > FIELD_GAP_RATIO * fontSize) text += " "; + else if (gap > SPACE_GAP_RATIO * fontSize && !/\s$/.test(text) && !/^\s/.test(span.text)) text += " "; + } + + text += span.text; + previous = span; + } + + return text.trim(); +} + +/** Mid-point of a convincing gutter, or null on a page that reads as one column. */ +function columnSplitX(page: PageGeometry): number | null { + const gutter = page.gutter; + if (!gutter || gutter.coverage < GUTTER_COVERAGE || gutter.splitRatio < GUTTER_SPLIT_RATIO) return null; + + return gutter.x + gutter.width / 2; +} + +/** + * Lines grouped by baseline alone interleave a sidebar with the body, which drops a whole work + * history into the skills section. On a two-column page each column is read out in full instead. + */ +function pageToLines(page: PageGeometry): string[] { + const splitX = columnSplitX(page); + if (splitX === null) return page.lines.map((line) => joinSpans(line.spans)); + + const left: string[] = []; + const right: string[] = []; + + for (const line of page.lines) { + const leftSpans = line.spans.filter((span) => span.x + span.width / 2 < splitX); + if (leftSpans.length > 0) left.push(joinSpans(leftSpans)); + + const rightSpans = line.spans.filter((span) => span.x + span.width / 2 >= splitX); + if (rightSpans.length > 0) right.push(joinSpans(rightSpans)); + } + + return [...left, ...right]; +} + +export function documentToLines(doc: ExtractedDocument): string[] { + return doc.pages.flatMap(pageToLines).filter((line) => line.length > 0); +} diff --git a/packages/pdf/src/semantic/test/rasterize-pdf.ts b/packages/pdf/src/semantic/test/rasterize-pdf.ts index 9d97f8134..92a67ae0c 100644 --- a/packages/pdf/src/semantic/test/rasterize-pdf.ts +++ b/packages/pdf/src/semantic/test/rasterize-pdf.ts @@ -1,4 +1,4 @@ -import { createCanvas } from "@napi-rs/canvas"; +import { fileURLToPath } from "node:url"; import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs"; export type RasterizedPdfPage = { @@ -7,40 +7,40 @@ export type RasterizedPdfPage = { data: Uint8Array; }; -type RasterPage = { - getViewport(input: { scale: number }): { width: number; height: number }; - render(input: unknown): { promise: Promise }; - cleanup(): void; +type RasterCanvas = { + canvas: HTMLCanvasElement; + context: CanvasRenderingContext2D; }; -type RasterDocument = { - numPages: number; - getPage(pageNumber: number): Promise; -}; - -type RasterLoadingTask = { - promise: Promise; - destroy(): Promise; +type RasterCanvasFactory = { + create(width: number, height: number): RasterCanvas; + destroy(canvas: RasterCanvas): void; }; export async function rasterizePdf(bytes: Uint8Array): Promise { - const loadingTask = getDocument({ data: bytes }) as unknown as RasterLoadingTask; + const loadingTask = getDocument({ + data: bytes, + standardFontDataUrl: fileURLToPath(new URL("standard_fonts/", import.meta.resolve("pdfjs-dist/package.json"))), + }); const pages: RasterizedPdfPage[] = []; try { const document = await loadingTask.promise; + // Use PDF.js's canvas implementation: a separately resolved native canvas can reject its Path2D objects. + const canvasFactory = document.canvasFactory as RasterCanvasFactory; for (let pageNumber = 1; pageNumber <= document.numPages; pageNumber += 1) { const page = await document.getPage(pageNumber); + let surface: RasterCanvas | undefined; try { const viewport = page.getViewport({ scale: 1.5 }); const width = Math.ceil(viewport.width); const height = Math.ceil(viewport.height); - const canvas = createCanvas(width, height); - const context = canvas.getContext("2d"); + surface = canvasFactory.create(width, height); + const { canvas, context } = surface; await page.render({ - canvas: canvas as unknown as HTMLCanvasElement, - canvasContext: context as unknown as CanvasRenderingContext2D, + canvas, + canvasContext: context, viewport, }).promise; pages.push({ @@ -49,6 +49,7 @@ export async function rasterizePdf(bytes: Uint8Array): Promise