CPS API – Council on Pharmacy Standards

CPS API

Standards-based endpoints for instant credential verification, partner integrations, and streamlined recertification evidence — built with security, auditability, and employer trust.

What the CPS API is for

A pragmatic API that reduces support, improves employer confidence, and unlocks integrations — while looking enterprise‑grade from day one.

Instant Credential Verification

Public, read‑only confirmation of a credential’s status with holder initials and last4 — no PDFs or screenshots required.

Verifiable Digital Badges

Host Open Badges / JSON‑LD assertions so credentials render reliably on LinkedIn and HR platforms.

Eligibility & Scheduling

Read‑only partner checks for eligibility status and available exam windows (UTC time with friendly “ET” label).

Recert Evidence Intake

Trusted providers can submit signed activity claims (presenter/reviewer/preceptor, employer in‑service, etc.) for candidate review.

Receipts & Letters

Self‑service retrieval of receipts and verification letters to cut support requests.

Webhooks

Push events like credential.issued or window.opened to partner systems with HMAC signatures.

Quickstart

  • Base URL: https://api.pharmacystandards.org/v1 (example)
  • Auth: Public endpoints are rate‑limited. Partner endpoints require API key or OAuth client‑credentials.
  • Time: All timestamps are ISO 8601 in UTC with a display label, e.g., display_tz: "ET".
  • Formats: JSON request/response; UTF‑8. Versioned via URL (/v1).

Public Endpoints

These endpoints avoid PII and are safe for employer lookups. Results include integrity headers for embedding.

Verify Credential

Confirm credential status using credential ID and a secondary check (initials+last4 or one‑time token).

# Request
GET /v1/credentials/verify?credential_id=CPOM-2025-000123&last4=4821

# 200 Response (example)
{
  "credential_id": "CPOM-2025-000123",
  "credential_name": "Certified Pharmacy Operations Manager (CPOM)",
  "holder": { "initials": "JD", "last4": "4821" },
  "status": "active",
  "issued_at": "2025-06-10T14:05:00Z",
  "expires_at": "2027-06-09T23:59:59Z",
  "verify_url": "https://pharmacystandards.org/verify/CPOM-2025-000123",
  "badge": {
    "png_url": "https://pharmacystandards.org/badges/cpom-000123.png",
    "assertion_url": "https://pharmacystandards.org/badges/abc123.json"
  }
}
# Header: X-CPS-Signature: hmac-sha256 of body using your public key id

Open Badge Assertion

GET /v1/badges/{assertionId}.json
{
  "@context": "https://w3id.org/openbadges/v3",
  "type": ["Assertion"],
  "id": "https://pharmacystandards.org/badges/abc123.json",
  "recipient": { "type": "email", "hashed": true, "salt": "...", "identity": "sha256$..." },
  "badge": "https://pharmacystandards.org/badges/cpom.json",
  "verification": { "type": "HostedBadge" },
  "issuedOn": "2025-06-10T14:05:00Z",
  "expires": "2027-06-09T23:59:59Z"
}

Partner Endpoints

Require API key (header) or OAuth 2.0 client‑credentials. Keys are scoped and revocable.

Eligibility Check

GET /v1/eligibility?candidate_ext_id=EXT-90210&cert_id=CPOM
Authorization: Bearer <token>
{
  "candidate_ext_id": "EXT-90210",
  "cert_id": "CPOM",
  "eligible": true,
  "window": {
    "start": "2025-09-01T00:00:00Z",
    "end":   "2025-09-30T23:59:59Z",
    "display_tz": "ET"
  },
  "notes": "Eligible pending ID verification."
}

Submit Recertification Evidence

POST /v1/recert/evidence
Authorization: Bearer <token>
Content-Type: application/json
{
  "candidate_ext_id": "EXT-90210",
  "cert_id": "CPOM",
  "activity_type": "presenter",
  "hours": 10,
  "start_date": "2025-04-18",
  "end_date":   "2025-04-18",
  "provider_id": "ACME-CONF-2025",
  "proof_url": "https://example.org/proofs/123",
  "signature": "hmac-sha256(...payload...)"
}

# 202 Accepted
{ "evidence_id": "EV-7f2b", "status": "received" }

Webhooks

Receive real‑time events: credential.issued, credential.revoked, window.opened, window.closed, recert.submitted, payment.received, exam.scheduled.

POST https://partner.example.com/hooks/cps
Headers:
  X-CPS-Event: credential.issued
  X-CPS-Signature: sha256=ab12cd...
Body:
{
  "event": "credential.issued",
  "id": "evt_47a",
  "created": "2025-06-10T14:05:05Z",
  "data": {
    "credential_id": "CPOM-2025-000123",
    "user_id": 91234
  }
}
Verify signatures (PHP example)
<?php
$secret = 'your_webhook_signing_secret';
$payload = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_CPS_SIGNATURE'] ?? '';

list($algo, $hash) = explode('=', $sig, 2);
$calc = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($calc, $hash)) {
  http_response_code(400);
  exit('Invalid signature');
}
http_response_code(200);
echo 'ok';

Authentication & Security

API Keys / OAuth

Provision per‑partner keys or OAuth client‑credentials; rotate and revoke in the CPS Developer Portal.

Data Minimization

Public endpoints expose initials+last4 only. No addresses, DOB, or full emails. Audit logs on all partner actions.

Time & Locale

Return UTC timestamps; include display_tz labels (e.g., "ET"). Avoid browser locale ambiguity.

Rate Limits & Errors

TypeLimitHeaders
Public verify60 req / minute / IPX-RateLimit-Limit, X-RateLimit-Remaining
Partner600 req / minute / keysame as above

StatusMeaningExample
400Validation error{ "error": "invalid_parameters", "details": {"last4": "required"} }
401Auth failed{ "error": "invalid_api_key" }
403Scope denied{ "error": "insufficient_scope" }
404Not found{ "error": "credential_not_found" }
409Conflict{ "error": "duplicate_evidence" }
429Rate limited{ "error": "rate_limited" }
500Server error{ "error": "server_error", "id": "req_abc123" }

Sandbox & Testing

  • Use https://api.sandbox.pharmacystandards.org/v1 with your sandbox key.
  • Test credentials are reset nightly; responses include X-CPS-Environment header.
  • Postman collection and OpenAPI (JSON) available below.

Build on CPS

Request access, explore the spec, and start verifying credentials in minutes.