AI Assistant & integrations

API & webhooks (developers)

Order checks, receive check and adverse-action events programmatically.

Settings → API — create keys for the REST API and webhooks. · click to enlarge

VolunteerBadge has a simple REST API for managing volunteers, sending applications, ordering background checks, and receiving real-time events. If you'd rather not write code, see Import volunteers for the spreadsheet importer, or Connect to Claude to drive everything in plain English.

Base URL

All endpoints live under https://www.volunteerbadge.com/api/v1. Requests and responses are JSON.

Authentication

Create an API key in Settings → API (you'll accept the CRA End-User Agreement the first time). Keys look like vb_live_… — treat them like a password and never expose one in client-side code. Send it as a bearer token on every request:

Authorization: Bearer vb_live_xxxxxxxxxxxxxxxxxxxx

Identity verification required first (live keys)

Before you can create a live API key or make a live API call, an owner or admin on the account must complete a free, one-time identity verification in Settings → Account (a quick government-ID + selfie check, about two minutes). Until then, live requests return 403 identity_verification_required. Sandbox test keys are exempt — see below — so you can start building right away.

Fair use & abuse monitoring

API and MCP traffic is rate-limited (100 requests per minute per key) and monitored for abuse — key guessing, volume spikes, and sustained error storms are flagged automatically. Abusive access may be suspended while we investigate; suspended requests return 403 api_access_suspended. Questions? support@screenforgelabs.com.

Sandbox / test mode

Build and test your whole integration — including webhooks — before going live, using a sandbox key. In Settings → API, click “Create sandbox test key”. Sandbox keys start with vb_test_ (live keys start with vb_live_) and can be created with no agreement and no identity verification.

A test key hits the same endpoints with the same request shape. In sandbox:

  • No credit is charged and no real check runs — nothing reaches the screening vendor.
  • You get an instant simulated response, and your registered webhooks fire for real (check.complete / check.error), so you can validate your handler end-to-end.
  • Nothing is persisted — POST /api/v1/volunteers and POST /api/v1/applications return simulated IDs without writing a roster row or sending an invite.

The outcome is deterministic from the subject you send (like a test card number):

Send thisSimulated result
lastName: "Records"Records found — result "consider" (fires check.complete)
SSN ending 0000Same — records found
lastName: "Error"The check fails (fires check.error)
anything elseClear result (fires check.complete)

Sandbox check IDs are prefixed chk_test_, and every sandbox response includes "mode": "test". When you're ready for production, generate a vb_live_ key (that one requires the CRA agreement + identity verification, since it furnishes real consumer reports) and run the exact same code.

Endpoints

Method & pathWhat it does
GET /api/v1/volunteersList volunteers (paginated, filter by status).
POST /api/v1/volunteersCreate a volunteer (idempotent on email).
POST /api/v1/applicationsSend a volunteer application by email, SMS, or shareable link.
POST /api/v1/checksOrder a background check for a subject (name + DOB + SSN + address).
POST /api/v1/checks/instantCertified no-SSN instant check (name + DOB + state) — requires authorizationOnFile + permissiblePurpose.
GET /api/v1/checks/{id}Get a check’s status and result.
GET /api/v1/creditsGet your remaining check-credit balance.

Create a volunteer

Push a person into VolunteerBadge — e.g. migrating a roster or syncing from another system. Posting the same email twice returns the existing record instead of duplicating it.

curl -X POST https://www.volunteerbadge.com/api/v1/volunteers \
  -H "Authorization: Bearer vb_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Jamie",
    "last_name": "Rivera",
    "email": "jamie@example.com",
    "phone": "443-790-8081",
    "dob": "1981-05-29",
    "state": "FL",
    "last_check_date": "2025-06-01",
    "fcra_authorization_on_file": true,
    "status": "approved"
  }'

# → { "volunteerId": "f883...", "created": true }

fcra_authorization_on_file is required

Set it to trueonly for people whose signed FCRA authorization you actually hold — it's what allows checks (and auto-rescreen) to run for them.

List volunteers

curl "https://www.volunteerbadge.com/api/v1/volunteers?status=approved&page=1&limit=25" \
  -H "Authorization: Bearer vb_live_..."

# → { "volunteers": [ { "id": "...", "first_name": "...", "status": "approved", ... } ],
#     "pagination": { "page": 1, "limit": 25, "total": 42, "totalPages": 2 } }

Order a background check

Requires firstName, middleName, lastName, dob, ssn, address, city, state, zip. middleName is required — instant checks match on name and date of birth without an SSN address trace, so a middle name is needed to confirm identity and reduce false matches on common names. This consumes 1 credit.

curl -X POST https://www.volunteerbadge.com/api/v1/checks \
  -H "Authorization: Bearer vb_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Jamie", "middleName": "Alex", "lastName": "Rivera",
    "dob": "1981-05-29", "ssn": "123456789",
    "address": "12112 Blue Hill Trail", "city": "Lakewood Ranch",
    "state": "FL", "zip": "34211"
  }'

# → { "checkId": "...", "status": "processing", "result": null, "message": "..." }

Poll GET /api/v1/checks/{id} for the result, or subscribe to the check.complete webhook (below) so you're notified instead of polling.

Certified instant check (no SSN)

Run an immediate check on firstName, middleName, lastName, dob, state with no SSN — for when you already hold the subject's signed FCRA disclosure and written authorization. Certify it per request with authorizationOnFile: true and a permissiblePurpose (one of volunteer_screening, employment_screening, youth_serving_organization). A middle name is required, and SSNs are rejected on this endpoint.

curl -X POST https://www.volunteerbadge.com/api/v1/checks/instant \
  -H "Authorization: Bearer vb_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Jamie", "middleName": "Lee", "lastName": "Rivera",
    "dob": "1981-05-29", "state": "FL",
    "permissiblePurpose": "volunteer_screening",
    "authorizationOnFile": true
  }'

# → { "checkId": "...", "status": "...", "result": "...", "certified": { ... } }

Identity verification required (newer accounts)

Instant checks via the API — POST /api/v1/checks and POST /api/v1/checks/instant — require your organization's owner to have completed a free, one-time identity verification first. Until then these calls return 403 with code: ID_VERIFICATION_REQUIRED. The owner verifies once in Settings → Account (“Verify my identity”). Existing accounts are grandfathered, and consent-first POST /api/v1/applications invites are never affected.

Errors

StatusMeaning
400Missing or invalid field — see the error message.
401Missing or invalid API key.
402Insufficient credits — buy more in Billing.
403Forbidden — the CRA End-User Agreement must be re-accepted (Settings → API), or (code ID_VERIFICATION_REQUIRED) the org owner must verify their identity before instant checks.
404Resource not found.
429Rate limited — slow down and retry.

Webhooks

Instead of polling, register a URL in Settings → Webhooks and VolunteerBadge will POST to it when things happen. Available events:

EventFires when
check.completeA background check finishes (clear or consider).
check.errorA check fails to process.
application.submittedA volunteer submits their application.
volunteer.createdA new volunteer record is created.
adverse_action.case_openedA report with records is released and VolunteerBadge opens an adverse-action case (or a case is first created for that check).
adverse_action.notice_sentA pre-adverse or final adverse notice is delivered by email (<code>data.stage</code> is <code>pre</code> or <code>final</code>).
adverse_action.dispute_openedThe consumer submits a dispute through the ScreenForge Labs portal link from the pre-adverse notice.
adverse_action.dispute_resolvedScreenForge Labs CRA staff finish reviewing a consumer dispute.
adverse_action.completedThe final adverse notice is sent and the case is complete.

Adverse-action webhooks include caseId, checkId, and volunteerId (null for direct checks). Notice and dispute events also include identifiers like noticeId, stage, or disputeId when available.

Each delivery is a JSON POST with two headers — X-VolunteerBadge-Event (the event name) and X-VolunteerBadge-Signature (an HMAC-SHA256 of the raw body, signed with your endpoint's secret):

POST  (your endpoint)
X-VolunteerBadge-Event: adverse_action.notice_sent
X-VolunteerBadge-Signature: 9f86d081...

{
  "event": "adverse_action.notice_sent",
  "data": {
    "caseId": "...",
    "checkId": "...",
    "volunteerId": "...",
    "stage": "pre",
    "noticeId": "...",
    "deliveryStatus": "delivered"
  },
  "timestamp": "2026-07-18T18:00:00.000Z"
}

Always verify the signature before trusting a payload. In Node:

import crypto from 'crypto';

function verify(rawBody, signatureHeader, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
}

Prefer no-code?

Native one-click connectors are already available for several popular nonprofit platforms (CRM, donor, church-management, and volunteer-management systems) — see the Integrations overview. Until your platform is listed there, the API above — or the spreadsheet importer — covers any sync you need.
Didn't find what you needed? Email support@screenforgelabs.com or browse all articles.