PrimaryCheck
Primary-source verification API

Verify the license before the hire.

One API call checks a license at the issuing board, an NPI at NPPES and a name against every exclusion list. Each claim comes back verified, discrepancy, not found or unable to verify, with the source that answered and a proof file.

Sandbox keys are immediate. Live keys follow FCRA customer vetting, usually one business day.

POST /v1/verifications
curl https://api.primarycheck.com/v1/verifications?wait=25 \
  -H "Authorization: Bearer pc_live_…" \
  -H "Idempotency-Key: 6f1c0b5e-…" \
  -H "Content-Type: application/json" \
  -d '{
    "person": {
      "firstName": "Amy", "lastName": "Kowalski", "dob": "1976-02-14"
    },
    "claims": [
      {
        "type": "healthcare_screen",
        "state": "PA", "profession": "rn",
        "licenseNumber": "RN353077L", "npi": "1003000126",
        "claimId": "hs"
      }
    ],
    "purpose": "employment",
    "consent": { "consentId": "cons_8f1…" },
    "reference": "ATS-48213"
  }'
How it works

Three steps from candidate to proof file

  1. 1

    Collect consent

    Send the candidate a hosted disclosure and authorization page, or attach your own signed one. No consent, no check.

  2. 2

    Submit the claims

    A license, NPI, exclusion screen, certification, degree or job, in one request. Pass ?wait=25 to long-poll or take a signed webhook.

  3. 3

    Read the verdict, file the proof

    Every claim carries field-by-field checks, the source record as published, hashes and a rendered PDF for the audit binder.

The response

A verdict you can act on, never a score

  • Four outcomes. verified, discrepancy, not_found, unable_to_verify, each with a machine-readable reason. Pending claims say so.
  • Field checks. Number, name, type, status, expiration and actions compared one by one. One mismatch is a discrepancy, with both values shown.
  • Evidence first. The raw source payload, a page capture and a rendered record PDF, hashed and timestamped, behind signed links.
  • Stale is not clear. Every dataset carries a data-as-of date. A list past its maximum age returns source_stale, never a pass.
How verdicts are decided
healthcare_screen · PA RN
{
  "id": "ver_7c1e0b1f2a9d4e8f0b3c",
  "status": "complete",
  "claims": [
    {
      "claimId": "hs",
      "type": "healthcare_screen",
      "verdict": "verified",
      "parts": [
        {
          "type": "license", "verdict": "verified",
          "summary": {
            "status": "Active - Multistate",
            "expires": "2028-04-30", "flags": []
          },
          "results": [
            { "source": "PA-PALS", "verdict": "verified",
              "evidence": ["evd_a1…", "evd_b2…"] }
          ]
        },
        {
          "type": "npi", "verdict": "verified",
          "results": [ { "source": "US-NPPES", "verdict": "verified" } ]
        },
        {
          "type": "exclusion_screen", "verdict": "verified",
          "results": [
            { "source": "US-OIG-LEIE", "verdict": "verified",
              "dataAsOf": "2026-10-30" },
            { "source": "US-SAM-EXCL", "verdict": "verified",
              "dataAsOf": "2026-11-03" },
            { "source": "US-OFAC-SDN", "verdict": "verified",
              "dataAsOf": "2026-11-03" },
            { "source": "PA-MEDICAID-EXCL", "verdict": "verified",
              "dataAsOf": "2026-11-01" }
          ]
        }
      ]
    }
  ],
  "completedAt": "2026-11-03T14:05:11Z",
  "fcra": { "controlsVersion": "fcra-controls-2026-11-03.0" }
}
For developers

Built the way you would build it

  • SDKs. pip install primarycheck and npm install primarycheck, generated from the OpenAPI spec, with helpers for waiting and signature checks.
  • Webhooks. HMAC-signed with a timestamp, retried with backoff, replayable from the dashboard.
  • Sandbox. A separate host with deterministic outcomes by last name and every live source code present.
  • Idempotency and versioning. Idempotency-Key on every POST; date-pinned behaviour changes; one error shape.
  • Coverage endpoint. GET /v1/coverage says what we can verify for a state and profession before you ask.
verify a webhook
from primarycheck import webhooks, SignatureVerificationError

@app.post("/primarycheck")                                  # FastAPI; Flask/Django are the same idea
async def hook(request: Request):
    try:
        event = webhooks.verify_signature(
            await request.body(),                           # the RAW bytes, before JSON parsing
            request.headers["PrimaryCheck-Signature"],
            WEBHOOK_SECRET,                                 # whsec_… from POST /v1/webhook_endpoints
        )
    except SignatureVerificationError:
        return Response(status_code=400)
    if event["type"] == "verification.completed":
        verification = event["data"]["object"]
    return Response(status_code=200)
Compliance

Run as a consumer reporting agency on every order

Verification for hiring, retention or credentialing is a consumer report under the FCRA. PrimaryCheck applies the full set of CRA controls to every order, whatever the stated purpose.

Start in the sandbox today

Same source codes as production. Go live after vetting, with no minimums and no platform fee.