Skip to main content

CV DeepSearch — Search (query the corpus)

Search is the second of the two CV DeepSearch flows: once you've filled a corpus, you run repeated searches against it for different positions. This guide covers triggering a search and consuming the run.

For the concepts, see the CV DeepSearch overview. The full endpoint reference is the Search group in the CV DeepSearch API reference sidebar.

Prerequisites

  • A corpus with at least one embedded candidate — see the Ingestion guide.
  • An API key with the cvdeepsearch permission.
  • (Optional) A publicly-reachable HTTPS endpoint, only if you want the completion webhook.

POST https://platform.zenhire.ai/api/v1/cvds/search

curl -X POST "https://platform.zenhire.ai/api/v1/cvds/search" \
-H "X-API-Key: zh_api_…" \
-H "Content-Type: application/json" \
-d '{
"corpus_id": "acme-eng-pool",
"position_id": "eng-backend-2026",
"position_metadata": {
"name": "Senior Backend Engineer",
"requirements": {
"workExperience": { "from": 5, "to": 10, "importance": 5, "relevant_industries": ["SaaS"], "industries_config": [{ "name": "SaaS", "importance": 5 }] },
"skills": { "importance": 5, "skills_config": { "hard_skills": [{ "name": "Node.js", "importance": 5 }], "requirements": { "minimal_qualifications": true, "preferable_qualifications": true } } }
}
},
"top_n": 50,
"webhook_url": "https://example.com/cvds/callback"
}'

Response (HTTP 200):

{ "id": "cvds_8mK2pQ7rT1aB9xY3wZ0vNn", "status": "pending" }

Save the id — you poll it (or correlate the webhook) to get the result.

The position_metadata config

position_metadata is the same shape CV DeepMatch consumes as its requirements config. The API requires only that it be a non-empty object; the inner shape is the search engine's contract. The full field-by-field contract — the 1–5 importance scale, where each requirement belongs, education ON/OFF, minimal vs preferable qualifications, the language-config quirks, and the cardinality limits — is in the Position config reference, with the per-field schema at CvDeepMatchRequirements.

job_updated & plan caching

The search planner caches a plan per position_id. Set job_updated: true only when the role's requirements changed since the last search (it rebuilds the plan); leave it false (default) to reuse the cached plan and re-rank.

top_n

top_n caps how many ranked candidates you want back. The server enforces an upper bound; values above the cap are clamped.

Idempotency

Pass an idempotency_key to make the trigger safe to retry — the same key replays the existing run id.

Consume the results

Two ways: poll (always available), or wait for the webhook (only if you supplied a webhook_url).

Option A — Poll

GET https://platform.zenhire.ai/api/v1/cvds/runs/{id}

curl "https://platform.zenhire.ai/api/v1/cvds/runs/cvds_8mK2pQ7rT1aB9xY3wZ0vNn" \
-H "X-API-Key: zh_api_…"
{
"id": "cvds_8mK2pQ7rT1aB9xY3wZ0vNn",
"status": "pending",
"corpus_id": "acme-eng-pool",
"position_id": "eng-backend-2026",
"is_job_updated": false,
"top_n": 50,
"source": "requests",
"created_at": "2026-06-09T10:00:00.000Z",
"results": null,
"results_pending": true
}

When results land, the candidate list is returned best-first — an ordered ranking, not a percentage match. Treat each result's score as a sort key, not a 0–100% score.

Results side is in progress

The best-first candidate list, the GET /api/v1/cvds/runs/{id}/results pagination endpoint, and the final webhook completion payload are finalized in a follow-up release. Until then the run poll returns the run's status with results: null and results_pending: true. The shapes below describe the provisional completion envelope; treat them as a forward reference, not a final contract.

Option B — Webhook callback (optional)

If you supplied a webhook_url, we POST a signed callback when the run finishes. The signing + retry mechanism is identical to CV DeepMatch: an HMAC-SHA256 signature in the X-CVDM-Signature header (t=<unix-ts>,v1=<hex>, Stripe-style), up to 3 attempts with exponential backoff, retried on 5xx / 429 / network errors.

You can register a webhook endpoint and send yourself a test cvdeepsearch completion event today from the self-serve webhook tool in your dashboard, even before the live completion payload is finalized — pick the cvdeepsearch.completed event type and hit Test.

Provisional completion payload shape (subject to change when results land — modeled on the CV DeepMatch completion callback):

{
"event": "cvdeepsearch.completed",
"id": "cvds_8mK2pQ7rT1aB9xY3wZ0vNn",
"corpus_id": "acme-eng-pool",
"position_id": "eng-backend-2026",
"status": "completed",
"completed_at": "2026-06-09T10:02:00.000Z"
}

Verifying the signature — use the same verifier as CV DeepMatch (the X-CVDM-Signature header has the form t=<unix-ts>,v1=<hex>, where <hex> is the lower-case hex HMAC-SHA256 of <unix-ts>.<compact-JSON-body>). See the CV DeepMatch verification snippet for the Node.js and Python implementations.

Error codes

The search endpoints use the shared standard error envelope:

error.codeHTTPMeaningRetry?
INVALID_INPUT400A field failed validation.After fix
MISSING_CORPUS_ID400A mandatory corpus_id was missing (fail-closed).After fix
INSECURE_WEBHOOK_URL400webhook_url is http://. Use HTTPS.After fix
PRIVATE_WEBHOOK_URL400webhook_url resolves to a private / loopback address.After fix
MISSING_PERMISSION401 / 403Missing/invalid key, or missing cvdeepsearch perm.Contact support
NOT_FOUND404No run with that id for your client.No
STEP_FUNCTIONS_FAILED502Downstream search pipeline failed to start.Yes, same idempotency_key
INTERNAL_ERROR500Uncategorised server error.After delay

Next steps

  • Read the Position config reference for how to shape position_metadata.
  • Browse the full CV DeepSearch API reference for every parameter, schema, and example.
  • For interactive exploration, use the Try it console on each endpoint reference page, or the API Sandbox in your dashboard.