Tiny CV

Documentation

Quickstart

Get a resume live in three moves.

Tiny CV is small on purpose: create a key, send a draft, publish it. Validation, schemas, claim links, and PDFs are there when you need them, not before.

1

Create a project and API key

No account required yet. Complete the challenge below to create your first project and reveal your live API key. Store it immediately, as it won't be shown again.

2

Create a draft resume

Send markdown or JSON to create a private draft resume. Tiny CV will return a resume ID that you'll use for all future moves.

import crypto from "node:crypto";

const response = await fetch("http://localhost:3000/api/v1/resumes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "input_format": "markdown",
  "markdown": "---\nstylePreset: technical\naccentTone: forest\ndensity: compact\nheaderAlignment: left\n---\n\n# Jordan Lee\nPrincipal Product Engineer\nSan Francisco, CA | [jordan@example.com](mailto:jordan@example.com) | [linkedin.com/in/jordanlee](https://linkedin.com/in/jordanlee) | [github.com/jordanlee](https://github.com/jordanlee)\n\n## Summary\nProduct engineer who turns ambiguous customer problems into reliable, revenue-facing software. Led 0-to-1 workflow, API, and frontend platform projects across early-stage and growth teams.\n\n## Experience\n### Principal Product Engineer | Northstar Labs\n*San Francisco, CA | 2022 - Present*\n- Led the rebuild of the onboarding and document generation flow, reducing time-to-first-publish from 38 minutes to 7 minutes for new teams.\n- Built a typed component and API contract layer used by 14 engineers, cutting production UI regressions by 42% over two quarters.\n- Partnered with design and GTM to launch an enterprise sharing workflow that supported six-figure expansion conversations.\n\n### Senior Software Engineer | Orbit Systems\n*Remote | 2019 - 2022*\n- Shipped a self-serve analytics surface used by 1,200+ weekly operators and replaced recurring custom SQL requests.\n- Improved page performance on core workflows, dropping p95 interaction latency from 820ms to 230ms.\n- Mentored 4 engineers and wrote the frontend testing guide adopted across product teams.\n\n## Projects\n### Tiny CV Agent Finish | Next.js, TypeScript, PostgreSQL\n- Designed an idempotent API flow that turns markdown into a hosted resume, claim link, and queued PDF job for autonomous agents.\n- Added OpenAPI discovery metadata for x402 and MPP clients while preserving bearer-token developer workflows.\n\n## Education\n### University of Washington | B.S. Computer Science\n*2015 - 2019*\n\n## Skills\nLanguages: TypeScript, Python, SQL\nFrontend: React, Next.js, Tailwind CSS, Accessibility\nBackend: Node.js, PostgreSQL, API Design, Queues\nProduct: Technical writing, customer discovery, launch planning",
  "return_edit_claim_url": true,
  "template_key": "founder",
  "title": "Jordan Lee Resume",
  "webhook_url": "https://example.com/tinycv/webhooks"
}),
});

const data = await response.json();
console.log(data);
3

Publish to the web

Publish your draft when you're ready for a public URL. You can also queue a PDF job to get a professional printable file.

Overview

The mental model is simple.

Tiny CV keeps one canonical markdown document, publishes a hosted resume page, and can hand you back a PDF on demand.

Markdown as truth

Markdown is the source of truth. JSON is just a nicer way to generate it.

Private by default

Drafts stay private. Public URLs appear only when you explicitly publish.

Async PDFs

PDFs are async. Request one when you actually need the artifact.

Optional claims

Claim links are optional. Use them only when a human should take over editing.

Playground

Try it live.

Pick an endpoint, add auth when needed, and inspect the real response without leaving the page.

POST /api/v1/resumes

Response

Run a request to inspect the response here.

API reference

Endpoints

Start with create and publish. The rest is there when your integration needs more than the happy path.

Getting started

Start in the docs for self-serve access, or use the protected bootstrap flow for managed provisioning.

POST

/api/v1/projects/bootstrap

Bootstrap a project (protected)

Protected bootstrap flow for managed or internal provisioning. The public documentation experience is the easiest way to create your first project and API key.

Auth: Bootstrap secret
const response = await fetch("http://localhost:3000/api/v1/projects/bootstrap", {
  method: "POST",
  headers: {
    "x-tinycv-bootstrap-secret": process.env.TINYCV_PLATFORM_BOOTSTRAP_SECRET!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "api_key_label": "Production Agent Key",
  "name": "Acme Recruiting Agent",
  "slug": "acme-agent"
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "apiKey": {
    "key": "tcv_live_xxxxxxxxxxxxxxxxxxxxxxxx",
    "keyPrefix": "tcv_live_xxxxxxxxx",
    "label": "Production Agent Key"
  },
  "project": {
    "createdAt": "2026-04-15T10:12:00.000Z",
    "id": "proj_123",
    "name": "Acme Recruiting Agent",
    "slug": "acme-agent",
    "updatedAt": "2026-04-15T10:12:00.000Z"
  },
  "webhookSecret": "tcv_wsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

Reference

Templates, schema discovery, and machine-readable docs.

GET

/api/v1/templates

List templates

List the built-in Tiny CV starter templates.

Auth: Public
const response = await fetch("http://localhost:3000/api/v1/templates", {
  method: "GET",
  headers: {
    
  },
  
});

const data = await response.json();
console.log(data);

Example response

{
  "templates": [
    {
      "badge": "Technical",
      "description": "Builder-first template.",
      "key": "engineer",
      "label": "Engineer"
    }
  ]
}
GET

/api/v1/templates/{key}

Fetch a single template

Fetch one template and its starter markdown.

Auth: Public
const response = await fetch("http://localhost:3000/api/v1/templates/designer", {
  method: "GET",
  headers: {
    
  },
  
});

const data = await response.json();
console.log(data);

Example response

{
  "badge": "Creative",
  "description": "A stronger portfolio-forward structure for designers.",
  "key": "designer",
  "label": "Designer",
  "markdown": "---\nstylePreset: creative\n...\n# Maya Chen"
}
GET

/api/v1/spec/markdown

Get markdown guide

Fetch the canonical markdown guide for Tiny CV resumes.

Auth: Public
const response = await fetch("http://localhost:3000/api/v1/spec/markdown", {
  method: "GET",
  headers: {
    
  },
  
});

const data = await response.json();
console.log(data);

Example response

{
  "format": "markdown",
  "guide": "# Tiny CV Markdown Guide\n..."
}
GET

/api/v1/spec/json-schema

Get JSON schema

Fetch JSON Schema for structured resume input.

Auth: Public
const response = await fetch("http://localhost:3000/api/v1/spec/json-schema", {
  method: "GET",
  headers: {
    
  },
  
});

const data = await response.json();
console.log(data);

Example response

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false,
  "properties": {
    "contact": {
      "items": {
        "additionalProperties": false,
        "properties": {
          "href": {
            "type": "string"
          },
          "kind": {
            "enum": [
              "email",
              "phone",
              "location",
              "url",
              "linkedin",
              "github",
              "x",
              "text"
            ],
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "required": [
          "kind",
          "value"
        ],
        "type": "object"
      },
      "type": "array"
    },
    "headline": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "sections": {
      "items": {
        "oneOf": [
          {
            "additionalProperties": false,
            "properties": {
              "paragraphs": {
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "type": "array"
              },
              "title": {
                "type": "string"
              },
              "type": {
                "const": "summary",
                "type": "string"
              }
            },
            "required": [
              "type",
              "paragraphs"
            ],
            "type": "object"
          },
          {
            "additionalProperties": false,
            "properties": {
              "entries": {
                "items": {
                  "additionalProperties": false,
                  "properties": {
                    "bullets": {
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "meta_left": {
                      "type": "string"
                    },
                    "meta_right": {
                      "type": "string"
                    },
                    "paragraphs": {
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "title": {
                      "type": "string"
                    },
                    "title_extras": {
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    }
                  },
                  "required": [
                    "title"
                  ],
                  "type": "object"
                },
                "minItems": 1,
                "type": "array"
              },
              "title": {
                "type": "string"
              },
              "type": {
                "const": "entries",
                "type": "string"
              }
            },
            "required": [
              "type",
              "title",
              "entries"
            ],
            "type": "object"
          },
          {
            "additionalProperties": false,
            "properties": {
              "bullets": {
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "type": "array"
              },
              "title": {
                "type": "string"
              },
              "type": {
                "const": "bullets",
                "type": "string"
              }
            },
            "required": [
              "type",
              "title",
              "bullets"
            ],
            "type": "object"
          },
          {
            "additionalProperties": false,
            "properties": {
              "groups": {
                "items": {
                  "additionalProperties": false,
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "value": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "label",
                    "value"
                  ],
                  "type": "object"
                },
                "minItems": 1,
                "type": "array"
              },
              "title": {
                "type": "string"
              },
              "type": {
                "const": "skills",
                "type": "string"
              }
            },
            "required": [
              "type",
              "groups"
            ],
            "type": "object"
          }
        ]
      },
      "minItems": 1,
      "type": "array"
    }
  },
  "required": [
    "name",
    "sections"
  ],
  "type": "object"
}

Draft and publish

Validate input, create drafts, update them, and publish public URLs.

POST

/api/v1/resumes/validate

Validate input

Validate markdown or JSON input without persisting anything. Use quality_gate: "publish" before publishing or making a paid Agent Finish call. In experience-like sections, use *Location, Remote, or website | Dates* on the italic metadata line.

Auth: Bearer token
const response = await fetch("http://localhost:3000/api/v1/resumes/validate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "input_format": "json",
  "quality_gate": "publish",
  "resume": {
    "contact": [
      {
        "kind": "email",
        "value": "maya@example.com"
      }
    ],
    "headline": "Product Designer",
    "name": "Maya Chen",
    "sections": [
      {
        "paragraphs": [
          "Designer with strong systems and product instincts."
        ],
        "type": "summary"
      },
      {
        "entries": [
          {
            "bullets": [
              "Led product design for a new workflow experience."
            ],
            "meta_left": "Remote",
            "meta_right": "2022 - Present",
            "title": "Senior Product Designer",
            "title_extras": [
              "Northstar"
            ]
          }
        ],
        "title": "Experience",
        "type": "entries"
      }
    ]
  },
  "style": {
    "accentTone": "plum",
    "stylePreset": "creative"
  },
  "template_key": "designer"
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "errors": [],
  "inferred_template_key": "designer",
  "normalized_markdown": "---\nstylePreset: creative\n...\n# Maya Chen",
  "publish_errors": [],
  "publish_ready": true,
  "quality_warnings": [],
  "valid": true,
  "warnings": []
}
POST

/api/v1/resumes

Create a draft

Create a Tiny CV draft from markdown or JSON. Returns the canonical markdown that Tiny CV stored. Send Idempotency-Key so retries do not create duplicates.

Auth: Bearer token· Idempotent
import crypto from "node:crypto";

const response = await fetch("http://localhost:3000/api/v1/resumes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "input_format": "markdown",
  "markdown": "---\nstylePreset: technical\naccentTone: forest\ndensity: compact\nheaderAlignment: left\n---\n\n# Jordan Lee\nPrincipal Product Engineer\nSan Francisco, CA | [jordan@example.com](mailto:jordan@example.com) | [linkedin.com/in/jordanlee](https://linkedin.com/in/jordanlee) | [github.com/jordanlee](https://github.com/jordanlee)\n\n## Summary\nProduct engineer who turns ambiguous customer problems into reliable, revenue-facing software. Led 0-to-1 workflow, API, and frontend platform projects across early-stage and growth teams.\n\n## Experience\n### Principal Product Engineer | Northstar Labs\n*San Francisco, CA | 2022 - Present*\n- Led the rebuild of the onboarding and document generation flow, reducing time-to-first-publish from 38 minutes to 7 minutes for new teams.\n- Built a typed component and API contract layer used by 14 engineers, cutting production UI regressions by 42% over two quarters.\n- Partnered with design and GTM to launch an enterprise sharing workflow that supported six-figure expansion conversations.\n\n### Senior Software Engineer | Orbit Systems\n*Remote | 2019 - 2022*\n- Shipped a self-serve analytics surface used by 1,200+ weekly operators and replaced recurring custom SQL requests.\n- Improved page performance on core workflows, dropping p95 interaction latency from 820ms to 230ms.\n- Mentored 4 engineers and wrote the frontend testing guide adopted across product teams.\n\n## Projects\n### Tiny CV Agent Finish | Next.js, TypeScript, PostgreSQL\n- Designed an idempotent API flow that turns markdown into a hosted resume, claim link, and queued PDF job for autonomous agents.\n- Added OpenAPI discovery metadata for x402 and MPP clients while preserving bearer-token developer workflows.\n\n## Education\n### University of Washington | B.S. Computer Science\n*2015 - 2019*\n\n## Skills\nLanguages: TypeScript, Python, SQL\nFrontend: React, Next.js, Tailwind CSS, Accessibility\nBackend: Node.js, PostgreSQL, API Design, Queues\nProduct: Technical writing, customer discovery, launch planning",
  "return_edit_claim_url": true,
  "template_key": "founder",
  "title": "Jordan Lee Resume",
  "webhook_url": "https://example.com/tinycv/webhooks"
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "created_at": "2026-04-15T10:14:00.000Z",
  "editor_claim_url": "https://tinycv.app/claim/claim_123?token=tcv_claim_xxx",
  "external_resume_id": null,
  "input_format": "markdown",
  "markdown": "# Jordan Lee\nPrincipal Product Engineer\n...",
  "pdf_url": null,
  "public_url": null,
  "published_at": null,
  "resume_id": "res_123",
  "status": "draft",
  "template_key": "founder",
  "title": "Jordan Lee Resume",
  "updated_at": "2026-04-15T10:14:00.000Z"
}
GET

/api/v1/resumes/{resume_id}

Get a resume

Read the current state of one draft or published resume.

Auth: Bearer token
const response = await fetch("http://localhost:3000/api/v1/resumes/res_123", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
  },
  
});

const data = await response.json();
console.log(data);

Example response

{
  "created_at": "2026-04-15T10:14:00.000Z",
  "input_format": "markdown",
  "markdown": "# Jordan Lee\nPrincipal Product Engineer\n...",
  "pdf_url": null,
  "public_url": null,
  "published_at": null,
  "resume_id": "res_123",
  "status": "draft",
  "template_key": "founder",
  "title": "Jordan Lee Resume",
  "updated_at": "2026-04-15T10:14:00.000Z"
}
PATCH

/api/v1/resumes/{resume_id}

Update a draft

Update an existing draft using the same input contract as draft creation.

Auth: Bearer token· Idempotent
import crypto from "node:crypto";

const response = await fetch("http://localhost:3000/api/v1/resumes/res_123", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "input_format": "json",
  "resume": {
    "headline": "Principal Product Engineer",
    "name": "Jordan Lee",
    "sections": [
      {
        "paragraphs": [
          "Product engineer who turns ambiguous customer problems into reliable, revenue-facing software."
        ],
        "type": "summary"
      },
      {
        "entries": [
          {
            "bullets": [
              "Reduced time-to-first-publish from 38 minutes to 7 minutes for new teams."
            ],
            "meta_left": "San Francisco, CA",
            "meta_right": "2022 - Present",
            "title": "Principal Product Engineer",
            "title_extras": [
              "Northstar Labs"
            ]
          }
        ],
        "title": "Experience",
        "type": "entries"
      }
    ]
  },
  "style": {
    "density": "compact"
  }
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "created_at": "2026-04-15T10:14:00.000Z",
  "input_format": "json",
  "markdown": "# Jordan Lee\nPrincipal Product Engineer\n...",
  "pdf_url": null,
  "public_url": null,
  "published_at": null,
  "resume_id": "res_123",
  "status": "draft",
  "template_key": "founder",
  "title": "Jordan Lee Resume",
  "updated_at": "2026-04-15T10:18:00.000Z"
}
POST

/api/v1/resumes/{resume_id}/publish

Publish a resume

Publish the current draft snapshot and get the public URL. Send Idempotency-Key so retries return the same publish result. API publish is strict: validate with quality_gate: "publish" first, fix malformed markdown, use *Location, Remote, or website | Dates* for experience metadata, and expect a 503 if browser fit measurement is unavailable.

Auth: Bearer token· Idempotent
import crypto from "node:crypto";

const response = await fetch("http://localhost:3000/api/v1/resumes/res_123/publish", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "return_edit_claim_url": true,
  "webhook_url": "https://example.com/tinycv/webhooks"
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "created_at": "2026-04-15T10:14:00.000Z",
  "editor_claim_url": "https://tinycv.app/claim/claim_124?token=tcv_claim_xxx",
  "input_format": "markdown",
  "markdown": "# Jordan Lee\nPrincipal Product Engineer\n...",
  "pdf_url": null,
  "public_url": "https://tinycv.app/SteadyBlueHeron",
  "published_at": "2026-04-15T10:20:00.000Z",
  "resume_id": "res_123",
  "status": "published",
  "template_key": "founder",
  "title": "Jordan Lee Resume",
  "updated_at": "2026-04-15T10:20:00.000Z"
}

Agent and MCP

Bearer-token project integrations and no-account paid Agent Finish calls.

POST

/api/v1/mcp

Call the MCP server

Remote MCP endpoint for agent tools, resources, and prompts over JSON-RPC. Mutating tools use JSON-RPC ids as idempotency keys when no Idempotency-Key header is present.

Auth: Bearer token
const response = await fetch("http://localhost:3000/api/v1/mcp", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/list",
  "params": {}
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "tools": [
      {
        "name": "tinycv_create_resume_draft"
      },
      {
        "name": "tinycv_publish_resume"
      }
    ]
  }
}

Artifacts and handoff

Artifacts, claim links, and export-related flows.

POST

/api/v1/resumes/{resume_id}/pdf-jobs

Queue a PDF job

Queue durable PDF generation for a published resume. Returns a pollable job immediately; webhooks are delivered from the worker outbox.

Auth: Bearer token· Idempotent
import crypto from "node:crypto";

const response = await fetch("http://localhost:3000/api/v1/resumes/res_123/pdf-jobs", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "webhook_url": "https://example.com/tinycv/webhooks"
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "completed_at": null,
  "error_code": null,
  "error_message": null,
  "job_id": "job_123",
  "pdf_url": null,
  "requested_at": "2026-04-15T10:21:00.000Z",
  "resume_id": "res_123",
  "status": "queued"
}
GET

/api/v1/pdf-jobs/{job_id}

Get PDF job status

Check PDF generation status and get a signed PDF URL once the artifact is ready.

Auth: Bearer token
const response = await fetch("http://localhost:3000/api/v1/pdf-jobs/job_123", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.TINYCV_API_KEY}`,
  },
  
});

const data = await response.json();
console.log(data);

Example response

{
  "completed_at": "2026-04-15T10:21:08.000Z",
  "error_code": null,
  "error_message": null,
  "job_id": "job_123",
  "pdf_url": "https://tinycv.app/api/v1/pdf-jobs/job_123/file?expires=...",
  "requested_at": "2026-04-15T10:21:00.000Z",
  "resume_id": "res_123",
  "status": "completed"
}
POST

/api/v1/edit-claims/{claim_id}/consume

Consume an edit claim

Consume a one-time edit claim and attach the resume into the current Tiny CV workspace.

Auth: Public
const response = await fetch("http://localhost:3000/api/v1/edit-claims/claim_123/consume", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "token": "tcv_claim_xxxxxxxxxxxxxxxxx"
}),
});

const data = await response.json();
console.log(data);

Example response

{
  "cleanEditorUrl": "/studio/res_123"
}

Resources

Reference files

Useful for agents, SDKs, and anyone who prefers a spec over vibes.

Markdown guide preview

# Tiny CV Markdown Guide

Tiny CV resumes are plain markdown with optional YAML frontmatter.

## File shape

```md
---
stylePreset: editorial
accentTone: forest
density: standard
headerAlignment: left
contactStyle: compact
pageMargin: 1
showHeaderDivider: false
showSectionDivider: true
pageSize: letter
---

# Alex Morgan
Founder & Product Engineer
San Francisco, CA | [alex@example.com](mailto:alex@example.com) | [linkedin.com/in/alexmorgan](https://linkedin.com/in/alexmorgan)

## Summary
Product-minded builder with experience across product, engineering, and go-to-market.

## Experience
### Founder | Meridian Labs
*Remote | 2023 - Present*
- Built the first product surface and shipped the company to revenue.

## Skills
Languages: TypeScript, Python, SQL
Frameworks: React, Next.js, Node.js
```

## Rules

- The first `#` heading is the candidate name.
- The next non-empty line is treated as the headline unless it looks like contact info. Keep it under 80 characters for publish-ready resumes.
- Contact info can be plain text or markdown links and is usually separated with `|`.
- Always include `## Summary` for publish-ready resumes.
- Top-level sections use `##`.
- Resume entries insi

...

Agent cookbook preview

# Tiny CV Agent Cookbook

## Happy path

1. Read the agent guide: https://tiny.cv/agents
2. Interview the user for missing facts.
3. Choose the best template for the target role.
4. Fetch the markdown guide or JSON schema.
5. Draft a one-page resume without inventing facts.
6. Validate the payload with `quality_gate: "publish"` before publish or payment.
7. Publish the draft only when the user wants a public link.
8. Request a PDF only when needed.

## Recommended workflow

- Use markdown if your agent already produces polished text.
- Use JSON if your agent starts from structured profile data.
- Do not invent employers, dates, credentials, metrics, or links.
- Default to the most recent ~4 substantive roles as full entries, then compress the rest.
- Each full role should usually have at least 2 meaningful lines. If a role only supports one thin line, move it to `Additional Experience` or cut it.
- Do not keep very old roles just to fill space. Keep them only when they materially strengthen the current narrative.
- Send an `Idempotency-Key` on create, update, publish, and PDF job requests.
- Experience entries should use `### Role | Company` and then `*Location, Remote, or website

...