CVPitch API, webhooks and MCP for developers
CVPitch developer docs: REST API, API keys and OAuth 2.1 with PKCE, scopes, endpoints, upload formats, signed webhooks, REST hooks and the MCP server.
The CVPitch API, together with signed webhooks and a remote MCP server, lets you connect candidate submission formatting to your ATS, CRM, automation platform or AI assistant. This page is an overview for developers and technical operations staff. For a non-technical summary, see integrations and the full list of features.
The design rule behind every interface: integrations can read metadata, upload CVs, request previews and export submissions a human has already approved, but nothing outside the CVPitch app can approve a submission.
Overview
| Item | Value |
|---|---|
| REST base URL | https://cvpitch.app/integrations/v1 |
| MCP endpoint | https://cvpitch.app/mcp |
| Authorisation server metadata | https://cvpitch.app/.well-known/oauth-authorization-server |
| Protected resource metadata | https://cvpitch.app/.well-known/oauth-protected-resource (REST) and .../oauth-protected-resource/mcp (MCP) |
| OpenAPI document | https://cvpitch.app/integrations/v1/openapi.json (OpenAPI 3.1, public) |
| Format | JSON, except document upload (multipart) and file download |
| Authentication | Authorization: Bearer YOUR_TOKEN with an API key or OAuth access token |
Browser session cookies do not authorise the API. Requests with an Origin header from an untrusted origin are refused.
Authentication
API keys
A workspace owner creates API keys in CVPitch and chooses their scopes. A key is shown once, stored hashed and can be revoked at any time. Treat it like a password: keep it in a secrets manager, never in a URL or a chat message. Each key acts on behalf of the member who created it, so access ends if that membership is removed.
OAuth 2.1 with PKCE
Third-party applications and AI assistants use the authorisation code flow with PKCE:
- Discover the endpoints from the authorisation server metadata.
- Send the user to
/oauth/authorizewithresponse_type=code, yourclient_id, an exact registeredredirect_uri, the requestedscope, astatevalue, acode_challengewithcode_challenge_method=S256, and aresourceparameter set to the REST base URL or the MCP endpoint. - The user signs in to CVPitch and sees a consent screen naming the workspace and the permissions requested.
- Exchange the code at
/oauth/tokenas a form post withgrant_type=authorization_code, thecode, yourclient_id, the sameredirect_uriandresource, and yourcode_verifier. Codes are single-use and expire quickly. - Use the access token as a bearer token. Tokens are short-lived and bound to one audience: a token issued for the REST API does not work on the MCP endpoint, and the reverse.
Only S256 is supported for PKCE, and public clients do not use a client secret.
Scopes
| Scope | Allows |
|---|---|
metadata:read | List templates, submissions, jobs, exports, usage and events. No candidate names or CV text. |
candidate:read | Read selected candidate facts with evidence, only when the workspace sharing policy is on |
drafts:write | Upload CVs, create upload links, suggest changes for human review, import a selected Lever attachment |
previews:write | Queue draft PDF previews |
exports:write | Export a submission that already has a valid human approval |
artifacts:read | Create short-lived download handles and download approved files |
Request the smallest set you need. A request without the right scope returns 403 with insufficient_scope and the required scope in the WWW-Authenticate header.
Endpoints
All paths are relative to https://cvpitch.app/integrations/v1.
| Method | Path | Scope | Purpose |
|---|---|---|---|
| GET | /me | any | The workspace, credential type and granted scopes; useful as a connection test |
| GET | /event-types | any | Every event type with a description |
| GET | /templates | metadata:read | List client templates with IDs and versions |
| GET | /usage | metadata:read | Read workspace usage for the period |
| GET | /submissions | metadata:read | List submissions (metadata only), with limit and offset |
| GET | /submissions/{id} | metadata:read | Stage, current revision, fingerprint and a review link |
| POST | /documents | drafts:write | Upload a CV as multipart form data |
| GET | /jobs/{id} | metadata:read | Processing job status |
| POST | /submissions/{id}/evidence | candidate:read | Read selected facts by field path, with masked evidence |
| POST | /submissions/{id}/proposals | drafts:write | Stage suggested changes for human review |
| POST | /submissions/{id}/previews | previews:write | Queue a draft PDF preview |
| POST | /submissions/{id}/exports | exports:write | Export an approved submission |
| GET | /exports/{id} | metadata:read | Export status and file IDs |
| POST | /artifact-handles | artifacts:read | Create a five-minute download handle for an approved file |
| GET | /artifacts/{handle} | artifacts:read | Download the approved DOCX or PDF |
| GET | /events | metadata:read | Poll events with a cursor, optionally filtered by type |
| GET, POST | /hooks | metadata:read (owner credential) | List or create REST hook subscriptions for one event type |
| DELETE | /hooks/{id} | metadata:read (owner credential) | Remove a REST hook subscription |
| POST | /connectors/import | drafts:write | Import a selected Lever attachment |
| POST | /connectors/attach-back | exports:write and artifacts:read | Attach an approved file to a Lever opportunity |
Typical flow
POST /documentswith the CV file and anIdempotency-Keyheader. The response contains asubmission_idand ajob_id.- Poll
GET /jobs/{id}, or listen for webhooks, until extraction finishes. - A recruiter reviews and approves the submission in CVPitch, using the
review_urlfromGET /submissions/{id}. POST /submissions/{id}/exportswith theexpected_revisionandfingerprintfrom the submission's metadata and anidempotency_key. If the submission changed or is not approved, you get409(for exampleapproval_stale).GET /exports/{id}until the export is ready, then create a handle withPOST /artifact-handlesand download the file within five minutes.
Idempotency
Operations that create work take an idempotency key of 1 to 100 characters: the Idempotency-Key header on uploads and the idempotency_key field on exports and Lever operations. Repeating a request with the same key and the same data returns the original result instead of doing the work twice. Reusing a key with different data returns 409 with idempotency_conflict. Generate a new key for each logical operation and reuse it only for retries.
Pagination and events
GET /submissions and GET /events accept limit from 1 to 100, and GET /submissions also takes an offset up to 10,000. GET /events takes an after cursor and returns items plus next_cursor; store the cursor and pass it on the next call. Events contain IDs, statuses and timestamps only.
Errors and limits
Errors use a consistent JSON shape: an error object with a code, a human-readable message and a request_id. Common statuses are 401 (missing, expired or revoked token), 403 (insufficient scope or a sharing policy not enabled), 404, 409 (stale revision, missing approval, idempotency conflict) and 429 (rate limited).
- Each credential can make up to 120 requests per minute.
- Uploads accept the same files as the app: PDF, DOCX, DOC, RTF, ODT and TXT (1 MiB at most), and PNG, JPEG, WebP or TIFF images. Files can be up to 10 MiB, PDFs up to 20 pages, multi-page TIFF scans up to 10 pages, and extracted text up to 120,000 characters. Your plan's upload allowance and per-workspace upload rate limits apply.
- File types are recognised by content: an unsupported type returns
415(unsupported_type) and a file that does not match its extension returns422(invalid_signature). - Scanned PDFs and images are read with OCR at 1 AI credit per page; text PDFs and Word files never use credits. An image upload is refused with
402(credits_exhausted) when the workspace has no credits left. A scanned PDF is detected during processing, so if credits run short its job fails with the same code. - Download handles expire after five minutes and only work with the credential that created them.
- MCP tool arguments are limited to 64 KiB and results to 512 KiB.
Webhooks
Webhooks deliver an HTTPS POST with a JSON body to your endpoint. Workspace owners add webhook endpoints in the app, or create REST hook subscriptions through the API. Destinations must be HTTPS addresses that resolve to public IP addresses; redirects are not followed. Event types are:
| Event | When it fires |
|---|---|
submission.ready_for_review | A CV finished processing and is ready for human review |
submission.approved | A recruiter approved the current revision |
export.completed | An approved DOCX and PDF pair is ready to download |
ats.pushed | Approved files were attached to a candidate record in a connected ATS or CRM |
share.created | A tracked client share link was created |
share.viewed | A client opened a shared submission |
share.feedback | A client left feedback on a shared submission |
talent.enquiry | A visitor sent an enquiry from the agency talent page |
job.queued, job.running, job.succeeded, job.cancelled | A processing or rendering job changed state |
job.failed | Processing or rendering failed (error code only) |
GET /event-types returns the current list. Payloads carry IDs, statuses, counts and relative links only, never candidate text.
REST hooks
Automation platforms that use REST hooks can subscribe without the app: POST /hooks with {"event": "submission.approved", "target_url": "https://..."} using an API key or OAuth token that belongs to a workspace owner. The response includes the hook ID and a signing secret, shown once. Deliveries are signed exactly like other webhooks. Remove the subscription with DELETE /hooks/{id}.
Verifying signatures
Each delivery includes three headers: X-CVPitch-Event (the event ID), X-CVPitch-Timestamp (Unix seconds) and X-CVPitch-Signature (the text v1= followed by a hex-encoded digest). To verify:
- Read the raw request body as bytes, before any JSON parsing.
- Build the signed string: the timestamp, a full stop, the event ID, a full stop, then the raw body.
- Compute HMAC-SHA256 of that string with your webhook signing secret and hex-encode it.
- Compare
v1=plus your result with theX-CVPitch-Signatureheader using a constant-time comparison. - Reject deliveries whose timestamp is outside your replay window, for example five minutes.
- Deduplicate on the event ID, because a delivery can be retried.
Respond with any 2xx status quickly and do slow work afterwards. Each event gets up to five delivery attempts, with increasing delays between them.
MCP server
The remote MCP server at https://cvpitch.app/mcp uses the Streamable HTTP transport with JSON responses over POST; the protocol version it supports is returned when your client initialises (currently 2025-11-25). Clients authenticate with an OAuth access token issued for the MCP resource. Tools currently include the following, listed according to the scopes granted:
| Tool | Scope |
|---|---|
search, fetch, get_workspace_summary | metadata:read |
list_templates, get_usage, list_submissions, get_submission_status, get_job_status, get_export_status | metadata:read |
get_candidate_evidence | candidate:read |
create_draft_preview | previews:write |
export_approved_submission | exports:write |
create_artifact_handle | artifacts:read |
create_upload_link, suggest_candidate_changes, import_selected_attachment | drafts:write |
search and fetch return submission metadata and review links, never candidate content. create_upload_link returns a short-lived link that opens the upload screen in the recruiter's signed-in browser, so no file passes through the assistant. export_approved_submission consumes an existing human approval and never grants one. suggest_candidate_changes stages a proposal for a recruiter to accept. Read the Model Context Protocol entry and our MCP guide for background.
Data handling for integrators
- Metadata endpoints never return candidate names or CV text.
get_candidate_evidenceand the evidence endpoint return only the field paths you request, with quotes masked according to the workspace sharing policy. Recruiter notes and raw source blocks are withheld.- Anything returned to an AI assistant is processed by that assistant's provider.
- Revoking a key, removing a member or deleting a submission ends access immediately.
Connectors and email intake
Native ATS and CRM connectors (Greenhouse, Lever, Workable, Ashby, Teamtailor, Recruitee, SmartRecruiters, Recruit CRM, Crelate and Manatal) and format-by-email are set up by a workspace owner in the app rather than through this API, and recruiters import and attach files from the app. Their results reach your systems through the same events: an imported or emailed CV produces submission.ready_for_review, and attaching approved files to an ATS produces ats.pushed. The older /connectors/import and /connectors/attach-back endpoints and the import_selected_attachment MCP tool use a separate Lever key saved under Integrations. See integrations for setup.
See security for the wider picture. API access is included on every plan; see pricing.
Frequently asked questions
Can the API approve a submission?
No. Approval is a human action in the CVPitch app, tied to the exact revision, template version and contact-removal policy. The API and MCP tools can only export submissions that are already approved.
Which events can I subscribe to?
Submission ready for review, submission approved, export completed, files attached to an ATS, share link created, viewed and answered, talent page enquiry, and job queued, running, succeeded, cancelled or failed. Payloads carry IDs, statuses and links only. Poll GET /events if you cannot receive webhooks.
Do you have SDKs or a Zapier app?
There are no official SDKs, and dedicated Zapier, Make and n8n apps are not published yet. The API is plain HTTPS and JSON with a public OpenAPI 3.1 document and REST hooks, so the platforms' generic webhook and HTTP steps work with it; see integrations.
How do I test without real candidate data?
Use synthetic CVs that you write yourself, never real candidates' documents, while you build. Start with a free trial workspace and create an API key with only metadata:read while you explore.
Turn your next CV into a client-ready submission
Upload a candidate CV, check every fact against the source, remove contact details and export your agency’s branded DOCX and PDF. Your first 10 CVs are free.