For developers and HR platforms
A verification API designed once for every state
A verification holds claims; each claim gets source results, a verdict and evidence. New states and check types add data, never endpoints. Build the integration once and coverage grows underneath it.
curl https://api.primarycheck.com/v1/verifications \
-H "Authorization: Bearer pc_live_…" \
-H "Idempotency-Key: 6f1c0b5e-…" \
-H "Content-Type: application/json" \
-d '{
"person": { "firstName": "Amy", "middleName": "E", "lastName": "Kowalski", "dob": "1976-02-14" },
"claims": [
{ "type": "license", "state": "PA", "profession": "rn", "number": "RN353077L", "claimId": "c1" },
{ "type": "npi", "number": "1003000126" },
{ "type": "exclusion_screen", "npi": "1003000126" }
],
"purpose": "employment",
"consent": { "consentId": "cons_8f1…" },
"reference": "ATS-48213"
}'API design
The parts you would have asked for
Async by default, fast path included
201 pending, then ?wait=25 long-poll or signed webhooks. Board and dataset checks usually finish inside one call.
Learn more
Idempotency on every POST
Idempotency-Key with a 24-hour window; same key with a different body is a 409.
Learn more
One error shape
type, code, message, param, requestId, docUrl. Every time.
Learn more
Sandbox parity
Deterministic outcomes by magic last name; every live source code present; webhooks fire.
Learn more
Coverage endpoint
GET /v1/coverage?state=&profession= answers what we can verify before you ask.
Learn more
Date-pinned versioning
Additive changes never break you; behavior changes are gated by PrimaryCheck-Version.
Learn more
SDKs
Python and Node, generated from the spec
primarycheck on PyPI and npm, with helpers for waiting on completion, auto-paging lists and verifying webhook signatures.
pip install primarycheck
from primarycheck import PrimaryCheck
pc = PrimaryCheck("pc_live_…") # base_url="https://sandbox.primarycheck.com" for pc_test_ keys
v = pc.verifications.create(
person={"firstName": "Amy", "middleName": "E", "lastName": "Kowalski", "dob": "1976-02-14"},
claims=[
{"type": "license", "state": "PA", "profession": "rn", "number": "RN353077L", "claimId": "c1"},
{"type": "npi", "number": "1003000126"},
{"type": "exclusion_screen", "npi": "1003000126"},
],
purpose="employment",
consent={"consentId": "cons_8f1…"},
reference="ATS-48213",
wait=25, # long-poll up to 25 s: board + dataset checks usually finish here
)
if v["status"] == "pending": # outreach can take days; webhooks are the better answer
v = pc.wait_for_completion(v["id"], timeout=600)
for c in v["claims"]:
print(c["type"], c["verdict"], c.get("reason"))Webhooks
Signed, timestamped, replayable
Every event carries a PrimaryCheck-Signature header: an HMAC over the timestamp and raw body. Reject anything older than five minutes. Deliveries retry with backoff and can be replayed from the dashboard.
from primarycheck import webhooks, SignatureVerificationError
try:
event = webhooks.verify_signature(raw_body, request.headers["PrimaryCheck-Signature"], WEBHOOK_SECRET)
except SignatureVerificationError:
return Response(status_code=400)
# event == {"id": "evt_…", "type": "verification.completed", "data": {"object": {...}}}How it works
Integration in four steps
- 1
Sandbox key
Create an account, copy a pc_test_ key, point at sandbox.primarycheck.com.
- 2
Model the claims
Map your candidate fields to license, healthcare_screen, education, employment and certification claims.
- 3
Handle verdicts
verified, discrepancy, not_found, unable_to_verify(reason), pending. Show the field checks; link the report PDF.
- 4
Go live
Apply for FCRA access in the dashboard; swap the key and host.
Developer documentation
Start in the sandbox today
Sandbox keys are immediate and return the same source codes as production.