Guide
Full parsing guide
How to go from a resume file to structured data: choose a submission flow, follow the job lifecycle, page through results, and handle credits, limits, and retries correctly.
What the parser returns
Backend engineer
data.nameSelect a field in either panel to see the connection. This is a result-data subset, not the complete API response.
GET /v1/schema reports parser version 1.0.3, standard mode, top-level field names (contact, summary, experience, education, skills, languages and certifications) and advertised evidence modes. It is a capability summary, not a complete nested JSON Schema. Read it when validating an integration; preserve unknown result fields and handle missing values.
The public submission path does not expose a selectable none/compact/full evidence response contract, despite those names in the summary. Do not rely on parse_options.evidence to change the returned shape. Precision and LLM-extra modes are not enabled on this standard public contract. A processing workspace can add its selected-field projection, evidence and validation result; see the workspace guide.
Flow 1 — direct multipart submission
Send files as multipart form data; successful admission returns data.job_id with HTTP 202, or HTTP 200 for a completed/cached result. Best for small batches and interactive tools.
curl -sS -X POST "$CVAULT_API_URL/v1/resumes/parse" \
-H "x-api-key: $CVAULT_API_KEY" \
-H "Idempotency-Key: batch-2026-09-14-0001" \
-F "files[]=@alice.pdf;type=application/pdf" \
-F "files[]=@bob.docx;type=application/vnd.openxmlformats-officedocument.wordprocessingml.document" \
-F "job_description=Backend engineer"The response returns data.job_id. Use GET /v1/parse-jobs/{job_id} to poll and GET /v1/parse-jobs/{job_id}/results to fetch the documents. POST /v1/parse-jobs is an alias of the same endpoint.
Flow 2 — upload sessions (large batches)
Upload sessions separate declaration, upload, and submission. Declare files up front, PUT each file to its signed URL, then complete the session. Use this for retries per file, parallel uploads, or when files must be uploaded independently. This is whole-file upload, not chunked resumable upload.
{
"files": [
{
"filename": "alice.pdf",
"size_bytes": 184320,
"content_type": "application/pdf"
},
{
"filename": "bob.docx",
"size_bytes": 96256,
"content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
}
],
"job_description": "Backend engineer",
"idempotency_key": "batch-2026-09-14-0001"
}POST the declaration as application/json to /v1/upload-sessions with a valid idempotency key. Use each file's real byte size, not the example value. The response includes one upload_targets[] entry per file with a signed signed_url.
# Repeat per upload_targets[] entry, using its own signed_url.
curl -sS -X PUT "<signed_url>" \
-H "content-type: application/pdf" \
--data-binary @alice.pdfcurl -sS -X POST "$CVAULT_API_URL/v1/upload-sessions/<session_id>/complete" \
-H "x-api-key: $CVAULT_API_KEY" \
-H "content-type: application/json" \
-d '{"idempotency_key":"batch-2026-09-14-0001-complete"}'- The declared
size_bytesmust match the uploaded object exactly. - Uploads must finish before
accept_before(10 minutes after declaration). - Completion validates actual type, size and malware scan evidence before admission. Size mismatch returns 415 unsupported_file. Do not send your API key to the signed storage URL. The 7,200-second provider token does not extend accept_before.
- Creation and completion both require keys; use distinct keys per operation. Completion can omit its body only if a key is supplied in a header. Retrying returns the bound job. Unknown IDs return 404; completing sessions 409; expired, cancelled or rejected sessions 410.
- Session job_description is limited to 50,000 UTF-8 bytes and normalized parse_options to 20,000 bytes.
Files and validation
| Rule | Value |
|---|---|
| Extensions | .pdf, .docx, .doc, .txt, .png, .jpg, .jpeg |
| Per file | Nonempty, at most 10 MiB; filename at most 255 characters |
| Aggregate file bytes | 50 MiB by default; deployment and account limits apply |
| Upload-session declaration | At most 1,000 files; direct uploads have separate admission limits |
| Content | Content/type, document resource limits and malware scan are validated |
Direct multipart rejection happens before admission. HTTP 413 means size/resource limits, 415 invalid type/content and 503 a required scanner outage. The direct JSON body contains success: false, admission_state: rejected_before_admission, retryable and redacted validation_results, with error_code when available. It is not a data/meta or problem+json envelope. Correct invalid input; retry transient scanner failures with the original submission key. Extension alone does not establish that a file is valid.
Idempotent retries
Networks fail; submissions must not duplicate work. Send Idempotency-Key on submission and completion.
- Required for multipart submission, upload-session creation and completion. Use Idempotency-Key, X-CVault-Idempotency-Key, or the idempotency_key form/JSON field. All supplied values must match; otherwise 400 idempotency_key_required.
- Keys are 8-200 characters, begin with a letter or digit and then use letters, digits, dot, underscore, colon or hyphen.
- Within a project, replaying the same key, operation, content and fields returns the recorded outcome. Keep the key across timeouts and process restarts; recorded responses may retain their original request ID.
- Changed content or fields with the same key returns 409 idempotency_conflict. An in-progress replay returns 409 idempotency_in_progress with Retry-After.
- Use distinct keys for session creation and completion. Cancellation and erasure are idempotent DELETE operations and need no idempotency header.
If submission is ambiguous, retain the exact bytes and options and retry with the same key rather than creating a new request.
Job lifecycle and polling
queuedAcceptedrunningProcessingsucceededInspect each filefailedcancelledtimed_outexpirederasingerasedJobs move through queued and running, then succeeded, failed, cancelled, timed_out or expired. The public status is succeeded rather than the internal done label. A succeeded batch can include failed or duplicate files; inspect every result. done counts processed files including failures and timeouts. estimated_seconds_remaining can be null and is not a latency guarantee.
curl -sS "$CVAULT_API_URL/v1/parse-jobs/<job_id>?include=results&limit=50" \
-H "x-api-key: $CVAULT_API_KEY"Nonterminal polls omit data.results. Terminal parsing polls include a results page object with its own results array and next_cursor; limit and cursor apply to that page. Choose an interval within your read allowance, for example two seconds with jitter, and a total deadline appropriate to your workflow. A client timeout does not cancel the server job. If a job ID is known, resume reads rather than uploading again. If an upload response was lost, retry the exact original bytes, fields and idempotency key.
Results and pagination
data.success describes retrieval. Each results[] item has its own status (succeeded or failed), success, filename, projected data and safe error when present. duplicate: true items have null data and success: false; duplicate_candidate_id identifies an existing candidate when present. Pending pages are not final: wait until the job is terminal before treating missing data as failure.
GET /v1/parse-jobs/<job_id>/results?limit=50
GET /v1/parse-jobs/<job_id>/results?limit=50&cursor=<URL-encoded-next_cursor>limit is 1-100 (default 50). Pass next_cursor as cursor, keep the same limit and API key, and stop only when next_cursor is null. Signed cursors are resource/project/environment/key-bound; never construct them or share them between jobs. duplicates describes only the current page. parses_used and parses_limit are workspace parse-quota counters, not organization parser credits. Internal raw_text, storage object keys, provider traces and internal parser configuration are stripped.
Credits and quota
Organization parser credits are reserved per admitted file: native processing costs 1 credit, OCR costs 3. Successful files settle their reservations; failed, timed-out and cancelled files release them. Insufficient credits return 402 insufficient_parser_credits. Workspace plan quota and project/key document caps are separate and can return 403 quota_exceeded.
GET /v1/usage returns metering events (outcome, lane, document_units and charged_units), not a balance. The current developer metering writer records charged_units: 0; organization parser credits are a separate ledger and are still consumed. Consult the console credit state for available credits. estimated_compute_usd is an estimate, not an invoice price.
Cancellation and erasure
DELETE /v1/parse-jobs/{job_id} cancels queued/running work and retains completed documents. Already-cancelled jobs return 200; other terminal states return 409 parse_conflict. DELETE /v1/parse-jobs/{job_id}/erasure requests source/result/cache cleanup. Neither operation requires an idempotency header. Erasure returns 200 erased or 202 erasing with cleanup_pending: true; poll until erased. Once accepted, result reads return 410 parse_job_erasure_in_progress, including while cleanup is pending. Cancellation is not deletion. Operational audit and metering records remain. Separately saved processing-workspace documents and exported or receiver copies require their own lifecycle handling; do not treat job erasure as organization-wide deletion.
Errors, retries, and troubleshooting
| Symptom | Cause | Action |
|---|---|---|
| 401 api_key_required / invalid_api_key | Missing or revoked key | Create or rotate the key in the console and update your environment. |
| 403 api_key_scope_required | Key lacks the endpoint scope | Mint a key with parse.write / parse.read as needed. |
| 409 idempotency_conflict | Same key, different payload | Reuse the original payload or use a fresh key. |
| 429 rate_limit_exceeded | Project minute limit reached | Honor Retry-After, add jitter, and batch requests. |
| 503 parse_service_unavailable | Transient parser outage | Retry with exponential backoff using the same idempotency key. |
