PasteApply

MCP HTTP Streamable remoto para adaptação honesta de currículo/carta de apresentação. Geração gratuita; desbloqueio via Stripe para downloads. MCP: https://pasteapply.com/mcp

Documentação

PasteApply for agents

Product: https://pasteapply.com

Listed on mcpservers.org

MCP happy path (bots: copy this loop)

Connect Remote MCP https://pasteapply.com/mcp (Streamable HTTP). Tools: status, store_resume, generate, unlock_link, confirm_payment, get_generation. Prefer MCP over HTTP twins and over driving the website.

  1. status — readiness + prices. Confirm Stripe/OpenAI are up. 1b. store_resume(resume) once (optional) → resumeId rs_1.<uuid> — on later generate, pass resumeId (prefer resumeId over re-pasting the full resume; pasted resume still wins if both are sent). TTL ~30 days. File uploads: HTTP POST /api/resumes multipart file (pdf/docx/doc/txt/md, max ~8MB).
  2. generate(jobPosting or jobUrl, resume or resumeId) — seconds later: teaser only when paid:false — coverage / gaps, fit (recommend: skip|weak|apply + mustHavesMissing / impliedOnly), recommendUnlock (false when fit.recommend is skip; true for weak|apply; optional unlockAdvice when false), proof (bulletsRephrased === visible original→tailored pair count), whatChanged / diff pairs, redacted sample. files.ready is false — no files.pdf / files.docx / files.coverPdf. No full letter, no full resume. Prefer pasted resume if both resume and resumeId are sent.
  3. Check recommendUnlock / fit.recommend before unlock. If recommendUnlock is false (or fit.recommend is skip), do not call unlock_link — missing must-haves cannot be invented. Otherwise unlock_link(price) — Stripe Checkout URL → hand to a HUMAN only. Never scrape Checkout. Prices: monthly=$3/1 · export=$5/5 (default) · pro=$10/50+Pro · power=$20/200 unlocks+Pro extras.
  4. poll get_generation(id) until paid:true — the Stripe webhook marks the draft paid and persists it; no cs_ ritual required on the happy path. Use confirm_payment(cs_ session id) only if the poll stays unpaid (slow webhook).
  5. When paid:true: full coverLetter, structured resume JSON, and paid files — files.ready, files.pdf, files.docx, files.coverPdf, files.expiresAt, files.coverLetter, files.resume. files.pdf / files.docx / files.coverPdf are absolute ?dl= URLs that last the UTC billing month (expire at UTC end of the current calendar month). Refresh anytime in-month via get_generation / confirm / paid generate (same generation still paid). If a file GET returns 403 (month ended), re-call get_generation for a fresh token (may 404/402 if the paid generation itself is past month-end TTL). Download Content-Disposition is LastCompany_Role.pdf / .docx and LastCompany_Role_cover.pdf (posting company+role, else latest experience; fallback Resume.pdf / Cover_Letter.pdf). files.coverPdf is /api/pdf/:id?kind=cover&dl=; alias GET /api/cover-pdf/:id.

Paid files object (machine shape):

{
  "ready": true,
  "pdf": "https://pasteapply.com/api/pdf/<id>?dl=<token>",
  "docx": "https://pasteapply.com/api/docx/<id>?dl=<token>",
  "coverPdf": "https://pasteapply.com/api/pdf/<id>?kind=cover&dl=<token>",
  "expiresAt": "2026-10-01T00:00:00.000Z",
  "coverLetter": "…full letter…",
  "resume": { "schemaVersion": 1, "name": "…", "contact": { "email": "…" }, "summary": "…", "skills": [], "experience": [{ "employer": "…", "title": "…", "start": "…", "end": "…", "location": "", "bullets": [] }], "education": [] }
}

Paid resume JSON: ATS schema v1 — schemaVersion:1, structured contact {email?, phone?, location?, links?} (not one blob), and every experience row has required employer|title|start|end|location|bullets (location "" if unknown; dates/employers frozen from source).

Honesty: never invent employers/dates/skills/metrics. If the posting wants something not on the resume, omit it. Guarantee: https://pasteapply.com/honesty · https://pasteapply.com/honesty.md

Quotas (UTC calendar month): $3/mo = 1 unlock · $5/mo Export = 5 unlocks · $10/mo Pro = 50 unlocks + hire-fit / variants · $20/mo = 200 unlocks + same Pro extras. No public promo codes.

Site ad unlock (humans only — AppLixir S2S): The website may offer Unlock free with ads after an unpaid generate when recommendUnlock is true (not skip), when AD_UNLOCK_ENABLED + AppLixir API key are live. The human watches AppLixir rewarded ads (SDK v6.1.0); client complete is optimistic UI only. The server grants only after AppLixir S2S GET /api/ad/applixir (MD5+TID signature, dedupe on tid) or generic HMAC POST /api/ad/reward, then marks that generation paid:true with the same artifacts as Stripe. MCP / bots / Bearer pa_… cannot ad-unlock — stay on Stripe: unlock_link → poll get_generation. No client-trust / AdSense grants; no LLM call on unlock.

Paid bots: Authorization: Bearer pa_… from confirm_payment / POST /api/confirm (apiKey) or POST /api/agent-key { "sessionId": "cs_…" } (confirm is fallback when you need an apiKey or the webhook is slow). Status/generate return unlocksUsed, unlocksRemaining, period (YYYY-MM); at cap generate → 402 code:"unlock_quota" with remaining:0 / unlocksRemaining:0 (not retry-later); free daily cap → 429 code:"free_cap"; bogus Bearer → 401 (no silent teaser).

Cursor mcp.json (hosted default — no provider key):

{
  "mcpServers": {
    "pasteapply": {
      "url": "https://pasteapply.com/mcp"
    }
  }
}

Heavy agents can BYOK through a secret connection header, never a tool argument or chat paste. Use an env-var placeholder — never a literal key. Rotate any key previously pasted into tool args or chat.

{
  "mcpServers": {
    "pasteapply": {
      "url": "https://pasteapply.com/mcp",
      "headers": {
        "X-PasteApply-BYOK-Key": "${OPENAI_API_KEY}"
      }
    }
  }
}

Some Cursor builds want "type": "http" next to the URL. Claude / other connectors: paste https://pasteapply.com/mcp as the remote MCP URL. Referral ?ref=CODE works on the site (see Referral); the MCP URL itself does not need ?ref=.

HTTP twins (secondary)

Base: https://pasteapply.com — status/generate/checkout work without auth (teaser). Paid bots use Authorization: Bearer pa_…. Same contract as MCP.

  • GET /.well-known/mcp.json → directory listing (name, mcp, docs, honesty)
  • GET /api → machine index (paths + prices)
  • GET /api/status → readiness JSON (openaiConfigured, stripeConfigured, googleAuthConfigured, demoMode, prices, byokSupported, byokProviders; with Bearer pa_… also plan/unlocks*/period/agentHints)
  • POST /api/resumes { "resume": "..." } or multipart file (pdf/docx/doc/txt/md, max ~8MB) → { "resumeId": "rs_1.<uuid>" }. Stores extracted text only (~30 day TTL). Cap ~20 stores/hour/IP. No listing of stored resumes.
  • POST /api/generate { "jobPosting", "resume" } or { "jobUrl", "resumeId" } or { "useSample": true } → generation.id + truncated teaser when paid:false. If both jobPosting and jobUrl, prefer pasted jobPosting. If both resume and resumeId, prefer pasted resume. Unknown/expired resumeId → 400. jobUrl must be public http(s); bad/blocked/failed fetch → 400 (not 500). Full bodies only after unlock. Optional proPasses for Pro. Optional BYOK: modelProvider (hosted default | openai | anthropic) in JSON; provider key only via header X-PasteApply-BYOK-Key (never in JSON body / tool args; body apiKey → HTTP 400). Hosted ignores the header. Honesty/fact-lock still applies on every path (vault text is source — never invent employers).
  • POST /api/checkout { "price": "monthly"|"export"|"pro"|"power", "generationId"?: "..." } → { "url" } — hand to HUMAN only (twin of unlock_link)
  • POST /api/confirm { "sessionId": "cs_..." } → fallback unlock + apiKey if poll stays unpaid (twin of confirm_payment)
  • POST /api/agent-key { "sessionId": "cs_..." } → { apiKey, plan, unlocksRemaining, unlocksUsed, period } for paid bots
  • GET /api/generation/:id → generation object including paid / unlocked (or 404 expired). Unpaid drafts persist ~24h; paid generations persist through UTC end of the calendar month. After pay, poll until paid:true (webhook persists — no confirm required on the happy path).
  • POST /api/webhook — Stripe signs this (ops only; bots do not call it). After pay, poll get_generation / GET /api/generation/:id until paid:true. Happy path stays unlock_link → poll get_generation.
  • POST /api/ad/session — human site only (browser Origin / same-site). Creates an ad session bound to generationId. Rejects Bearer pa_ / MCP. Bots must not call this.
  • POST /api/ad/reward — ad-network S2S callback (shared secret / HMAC). Not for bots or browsers.
  • GET /api/ad/session/:id — human UI progress only.

curl

curl -s https://pasteapply.com/api/status
curl -s -X POST https://pasteapply.com/api/resumes -H 'content-type: application/json' -d '{"resume":"..."}'
curl -s -X POST https://pasteapply.com/api/generate -H 'content-type: application/json' -d '{"jobPosting":"...","resume":"..."}'
curl -s -X POST https://pasteapply.com/api/generate -H 'content-type: application/json' -d '{"jobUrl":"https://example.com/jobs/1","resumeId":"rs_1...."}'
curl -s -X POST https://pasteapply.com/api/generate -H 'content-type: application/json' -H 'X-PasteApply-BYOK-Key: $OPENAI_API_KEY' -d '{"jobPosting":"...","resume":"...","modelProvider":"openai"}'
curl -s -X POST https://pasteapply.com/api/checkout -H 'content-type: application/json' -d '{"generationId":"GENERATION_ID","price":"export"}'

Exception

If the human owns PasteApply or banned spend asks: preview-only.

Referral (gift months, not cash)

https://pasteapply.com/?ref=CODE sets a 30-day pa_ref cookie.

Paying referrer → matching-tier month for the friend: if the code owner has a live Stripe subscription (active / trialing), Checkout for that same tier applies a 30-day trial (subscription_data.trial_period_days: 30) on the matching Price. After the trial, Stripe bills the regular monthly price. They can cancel. Self-referrals and free/teaser-only accounts do not grant a trial.

The referred person's first successful payment also banks the referrer a matching month: $3 → a month of the $3 plan, $5 → a month of Export, $10 → a month of Pro. Self-referrals are ignored. Unknown codes do not pay out. Not cash.

Referrers bank unused gifted months — max 12 total. At 12 they are capped and cannot earn more until they consume at least 1 banked month (time passing, or gifting a month). Then they can earn again, still max 12.

Gift a banked month to a friend anytime the giver has ≥1 banked month: giver −1, friend +1 of the same plan. Friend must already have a PasteApply Google account (email that resolves to a Google sub). No self-transfer. Friend also max 12 — reject if they are already capped.

Checkout and unlock_link still stamp referralCode / referredBy. Signed-in humans gift from the Share panel (POST /api/referral/gift).

Endpoints (detail)

GET /api/status

Returns JSON: openaiConfigured, stripeConfigured, googleAuthConfigured, demoMode, subscriptionActive, proActive, prices ($3/mo · 1 unlock, $5/mo · 5 unlocks, $10/mo Pro · 50 unlocks, $20/mo · 200 unlocks), byokSupported, byokProviders (openai, anthropic). With Authorization: Bearer pa_…: also plan, unlocksUsed, unlocksRemaining, period (YYYY-MM), agentHints (e.g. reuse resumeId — don't re-paste the full resume). Bogus Bearer → 401. No secrets. CORS * (no credentials).

POST /api/resumes

Content-Type: application/json or multipart/form-data

{ "resume": "Jane Doe\nAcme 2020-2024 Operations Analyst\nSQL" }

Or multipart field file (.pdf / .docx / .doc / .txt / .md, max ~8MB) — we extract text via the same path as /api/extract-resume and store text only (no polish / no invented employers).

Response: { "resumeId": "rs_1.<uuid>" }. Reuse resumeId on generate. Soft TTL ~30 days (unknown/expired → generate 400). Cap ~20 stores/hour/IP. ResumeId is a capability (unguessable) — there is no list endpoint.

POST /api/generate

Content-Type: application/json

{
  "jobPosting": "...",
  "jobUrl": "https://example.com/jobs/1",
  "resume": "...",
  "resumeId": "rs_1.<uuid>",
  "useSample": false,
  "proPasses": [],
  "modelProvider": "hosted",
  "model": ""
}

Header (BYOK only): X-PasteApply-BYOK-Key: ${OPENAI_API_KEY} — env-var placeholder, never a literal key in docs or chat.

useSample: true loads the public demo. Identical job+resume (same fingerprint) returns the same generation.id and debits at most one unlock / free daily consume. proPasses only applies for Pro members. Pass jobPosting text and/or jobUrl (public http(s) only; we fetch and strip HTML; prefer pasted jobPosting if both). Pass resume text and/or resumeId from POST /api/resumes / MCP store_resume (prefer pasted resume if both). Unknown/expired resumeId → HTTP 400. Bad/blocked URL, fetch failure, empty/non-HTML body → HTTP 400 with a clear message (not 500). Counts toward the free daily generate cap like a pasted job.

Optional BYOK (server-side only): hosted is default. Heavy agents set modelProvider to openai or anthropic and send the provider key only in X-PasteApply-BYOK-Key (HTTP header / MCP connection headers). Never place provider keys in tool arguments, JSON body, or chat. JSON body apiKey is rejected (HTTP 400). Hosted ignores the header and never lets the client override our model (cost lock). BYOK uses the user's tokens and may set model. The key is never logged, persisted, hashed, echoed, or returned. Rotate a key if it was previously pasted. Fact-lock still runs on every path and fails closed (HTTP 422). Pricing ladder unchanged ($3=1 / $5=5 / $10=50 / $20=200).

Free vs paid payload

Unpaid (paid: false, unlocked: false, preview: true): incomplete teaser only. Keep coverage, fit, recommendUnlock (and unlockAdvice when skip), proof / factLock (bulletsRephrased = visible pair count — equals the number of original→tailored pairs shown), and whatChanged / diff with every original → tailored pair (original is the source line; tailored may be shortened). Redacted sample (coverLetter is the first sentence with [redacted]; resume keeps name + first role header + 1–2 redacted bullets). No full cover letter, no full tailored resume, no snapshot. Unpaid files.ready is false — no files.pdf / files.docx / files.coverPdf.

Paid (paid: true, unlocked: true, preview: false) after Checkout confirm or an active plan still under its monthly unlock quota: full coverLetter, full resume, clean files, and snapshot. Paid files: files.ready (true), files.pdf, files.docx, files.coverPdf, files.expiresAt, files.coverLetter, files.resume. Download tokens last the UTC billing month (HMAC ?dl= expires at UTC end of the calendar month); refresh via get_generation anytime in-month. Paid generation Blob TTL also extends to UTC month end (unpaid teasers stay ~24h). On file GET 403, re-call MCP get_generation / GET /api/generation/:id for a fresh token. Unpaid keeps files.ready false and omits pdf/docx/coverPdf. Pro extras (hire-fit, variants, interview, scorecard) on $10 Pro or $20/mo (200 unlocks).

MCP generate is the same contract. Do not stitch the teaser into a file. Check recommendUnlock / fit.recommend first — if skip / recommendUnlock:false, do not unlock. Otherwise call MCP unlock_link (default $5 Export = 5 unlocks/mo) and hand the URL to a human.

Stable fields: generation.id, coverLetter, resume, coverage, fit (always: recommend skip|weak|apply, mustHavesMissing, impliedOnly — gaps only, never invents), recommendUnlock (false when fit.recommend===skip; true for weak|apply; optional unlockAdvice when false), whatChanged (same as diff), proof, factLock (human line), quality, paid, unlocked, preview, estimate (~10s), files. CORS * (no credentials). Prefer server-side fetch.

Machine-readable honesty (also on MCP generate and GET /api/generation/:id):

{
  "proof": {
    "factLock": true,
    "inventedClaims": 0,
    "bulletsRephrased": 4,
    "skillsLed": 2,
    "promise": "We only rearrange what is already true."
  },
  "coverage": [
    { "skill": "SQL", "status": "present", "note": "Already on the source resume. We can lead with this." },
    { "skill": "Salesforce admin", "status": "implied", "note": "Salesforce is on the resume; the exact admin title is not." },
    { "skill": "Kubernetes", "status": "missing", "note": "Kubernetes is not on the resume. Lead with SQL — do not invent Kubernetes." }
  ]
}

coverage[].status is present | implied | missing. Every generate also returns fit: deterministic skip|weak|apply from coverage (any missing must-have ⇒ skip; honesty still strips invented skills separately). Agent views also include recommendUnlock (false on skip; true on weak|apply) and optional unlockAdvice when false — check before unlock_link. Present = named on the source resume. Implied = a related tool or title is on the resume, not the exact word. Missing = not on the resume — we show the gap and do not invent it. If inventedClaims would be >0, generate fails (HTTP 422) instead of returning fiction. Guarantee: https://pasteapply.com/honesty

POST /api/checkout

{ "price": "export", "generationId": "<uuid>" }

price: monthly | export | pro | power — required. $3/mo = 1 unlock (price: "monthly"). $5/mo = 5 unlocks (price: "export", default). $10/mo Pro = 50 unlocks + extras (price: "pro"). $20/mo = 200 unlocks + same Pro extras (price: "power"). Empty body, missing price, or bogus price → HTTP 400. generationId optional. Website checkout type for $20 is power ($20=200).

Response: { "url": "https://checkout.stripe.com/..." } — give this link to a human.

POST /api/confirm

{ "sessionId": "cs_live_..." }

Fallback unlock after Checkout when get_generation still shows paid:false (slow webhook). On success includes apiKey (pa_1.…) for paid-bot Bearer auth. Twin: POST /api/agent-key with the same sessionId. Happy path: poll GET /api/generation/:id / MCP get_generation until paid:true — no cs_ required.

GET /api/generation/:id

Returns the generation object plus paid / unlocked, proof, and coverage, or 404 if expired or unknown. Unpaid drafts persist ~24h; paid generations persist through UTC end of the created calendar month. Poll this (or MCP get_generation) right after generate; you should get the teaser, not 404. Unpaid responses stay teasers. After the human pays, the Stripe webhook marks the draft paid and persists it — poll until paid: true for the full payload (confirm only if the poll stays unpaid).

Rate limits

Public teaser path needs no key. Paid bots use Authorization: Bearer pa_…. Free teasers are capped per IP and per browser session (UTC day). Paid plans are capped by UTC calendar-month unlocks: $3 = 1, $5 = 5, $10 = 50, $20 = 200. At cap, generate returns HTTP 402 with a human upsell message.

  • Real job+resume teasers: 15 / day
  • Sample (useSample: true): 60 / day

Exceed free cap: HTTP 429, Retry-After (seconds until the next UTC midnight), and JSON:

{ "error": true, "code": "free_cap", "message": "Daily free generate limit reached (15 real job+resume teasers per UTC day). Try again in about 12h (retryAfter seconds until UTC midnight). Or unlock with $3/mo (1 unlock), $5 Export (5), $10 Pro (50), or $20/mo (200) this calendar month (UTC).", "retryAfter": 43200 }

Show message to the human. Honor retryAfter / Retry-After. If you get 429, 502, or 504, wait (or backoff 1s, 2s, 4s on 502/504). Do not hammer generate. Prefer one generate at a time per client.

Paid unlock month cap (not retry-later): HTTP 402 and JSON:

{ "error": true, "code": "unlock_quota", "message": "…", "remaining": 0, "unlocksRemaining": 0, "unlocksUsed": 1, "period": "2026-09", "plan": "starter" }

Do not treat 402 / unlock_quota as wait-and-retry — upsell or stop.

Errors and retries

JSON uses human-readable message (or error string on checkout/confirm) — show that text to the human; do not invent status copy.

  • 400 — bad JSON, empty job/resume (unless useSample or valid resumeId), unknown/expired resumeId, bad/blocked jobUrl / fetch fail, or confirm with a bogus/unknown Checkout sessionId (not 502). Example: "Paste a job posting (or jobUrl) and your current resume." / "Unknown or expired resumeId…" / "That job URL is blocked (local or private network). Paste the posting text instead." / "That Checkout session was not found."
  • 422 — honesty lock: invented claims. Fix the source resume; do not retry the same invention.
  • 429 — free generate daily cap (code:"free_cap"; 15 real / 60 sample per UTC day). Wait for retryAfter / Retry-After (seconds until UTC midnight). Never treat as a paid-wall.
  • 403 — download ?dl= token expired (UTC billing month ended). Re-call MCP get_generation / GET /api/generation/:id for a fresh files.pdf / files.docx / files.coverPdf URL; do not DIY the file.
  • 402 — unpaid PDF/DOCX/cover download (code:"payment_required"), or paid plan at monthly unlock cap (code:"unlock_quota", remaining:0 / unlocksRemaining:0, plus used/period/plan; LIVE-UNVERIFIED). Show message. Upsell the next tier; do not retry-later (402 ≠ 429). Do not invent a promo code.
  • 404 — generation expired (unpaid ~24h; paid UTC month end) or unknown id
  • 504 — generate took too long; try again in a moment
  • Standard generate aborts at ~20s; Pro rewrite ~28s. estimate is ~10s.

Input size

Job posting max 20,000 characters. Resume max 40,000 characters. Longer bodies return 400. On the $20 plan, hosted Pro accepts at most 46,380 characters of resume + job posting combined (Standard and BYOK keep the full limits).

MCP tool schemas

Remote MCP https://pasteapply.com/mcp (Streamable HTTP). Tools:

  • status — no args. Readiness + prices (+ plan/unlocks when inbound Authorization: Bearer pa_…).
  • store_resume — resume text → { resumeId } (rs_1.<uuid>). Store once; prefer resumeId on later generate over re-pasting. Files: HTTP multipart POST /api/resumes (not this tool).
  • generate — jobPosting?, jobUrl?, resume?, resumeId?, useSample?, proPasses?, optional BYOK modelProvider? / model? (no apiKey in tool args). Provider key only via connection header X-PasteApply-BYOK-Key. Prefer resumeId over re-pasting after store_resume; pasted resume / jobPosting still win if both pairs sent. Forwards inbound Bearer + BYOK header. Unpaid: teaser JSON + proof + recommendUnlock/fit (not the full letter). Paid key with remaining>0: full cover + resume + files (consumes 1 unlock). Check recommendUnlock before unlock_link.
  • unlock_link — only when recommendUnlock is true (skip when fit.recommend is skip). generationId?, price? (monthly = $3/mo · 1 unlock | export = $5/mo · 5 unlocks, default | pro = $10/mo · 50 unlocks + Pro | power = $20/mo · 200 unlocks + Pro extras). Stripe Checkout URL for a human. After pay, poll get_generation until paid:true for paid files.* (ready, pdf, docx, coverPdf, expiresAt, coverLetter, resume). confirm_payment only if the poll stays unpaid.
  • confirm_payment — sessionId (cs_...). Fallback when webhook is slow. After success, payload is the full artifact + paid files + apiKey.
  • get_generation — id or generationId (same uuid). Happy path after pay: poll until paid:true. Teaser until paid. When paid, returns full cover + resume + files (ready, pdf, docx, coverPdf, expiresAt, coverLetter, resume) with tokens lasting the UTC billing month. On file 403, re-call for a fresh token.

Browser GET Accept: text/html on /mcp returns a short hint page linking here. MCP clients should send JSON / event-stream, not HTML.

Jobs board (humans)

Human site surface: /jobs (search), /jobs/{id} (detail), /jobs/employers (post/manage). Owned PasteApply listings always. Outside results (qualifying paid plans only: $5 Export 20 searches/mo, $10 Pro and $20 75/mo per account, UTC month): GET /api/jobs?q=…&location=…&web=1 with a signed-in paid session runs the OpenAI Responses web_search tool restricted to public ATS job boards (Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Jobvite, iCIMS and similar); every URL is re-validated server-side and only real source/citation listing URLs are returned (source:"web", external:true, applyUrl). Cached by normalized query+location (cache hits don't count). Outside results are link-checked before they are shown and cached for up to 12h, so a listing can still close within that window. Free/$3 → owned posts + one upsell line (webSearch.upsell). No competitor scraping: never Indeed/LinkedIn/Glassdoor/Google scrapes, no SerpAPI. Interview % for an outside result: POST /api/jobs/{ws_id}/interview-chance with {resume, jobUrl: applyUrl}, or tailor via /?jobUrl={applyUrl} for before/after. Free posts: signed-in Google employers draft → Publish (POST /api/jobs/{id}/publish) marks open + expiresAt = now+30d — no Stripe, no 402. Rate-limited per employer UTC day; open-seat cap still applies. Optional Promote: $30 one-time for 30 days featured (env STRIPE_PRICE_JOB_PROMOTE=price_1UILnOH8RUFIVCIaPHLm1gMa, lookup pasteapply_job_promote_30d_30). Checkout metadata.type=job_promote → webhook sets promotedUntil; public board sorts featured first + badge. Unpromoted open jobs still listed. MCP stays tailor tools (status / store_resume / generate / unlock_link / …). Do not invent a search_jobs MCP tool until a real inventory API exists. Bridge: job detail → /?jobUrl=https://pasteapply.com/jobs/{id} (generate resolves owned URLs from Blob; honesty lock + unlock ladder unchanged).

Version

Contract v0. Changelog: site ad-funded unlock (humans only; bots stay Stripe); BYOK provider keys move to X-PasteApply-BYOK-Key (never tool args / JSON body); public $20 plan id is power ($20=200); resume vault store_resume / POST /api/resumes → resumeId rs_1.<uuid> (~30d); generate accepts resumeId; paid-bot Authorization: Bearer pa_… (confirm / /api/agent-key); UTC-month unlock quotas $3=1 / $5=5 / $10=50 / $20=200 + Pro extras; at cap generate is 402 + unlock_quota; free daily cap is 429 + free_cap; starter unlock is $3/month (price: "monthly"); default human unlock remains $5 Export; free generate daily caps (15 real / 60 sample) return 429; unpaid generate is a truncated teaser; paid unlock returns the full artifact + clean files; honesty proof + coverage (present / implied / missing); referral gift months (max 12); Drive save; remote MCP happy path status → optional store_resume → generate → unlock_link → poll get_generation until paid:true (confirm_payment fallback only). No public promo codes.

Rules for operators

  • Prefer MCP (https://pasteapply.com/mcp) over HTTP twins; prefer either over driving the website.
  • Never invent employers, dates, skills, or metrics.
  • Never complete Stripe Checkout as the user.
  • Never call /api/ad/* as a bot — site ad unlock is humans only; bots stay Stripe paid.
  • Site for humans: https://pasteapply.com