Overview
Peabody Secure provides identity verification (KYC) and AML/sanctions screening as a hosted service.
The verification flow consists of three steps:
- ID Upload — the user photographs the front and back of their driver's license. The back barcode is parsed automatically.
- Liveness Check — the user completes a short challenge (blink, smile, etc.) captured via webcam.
- Face Match — the selfie from the liveness check is compared to the photo on the ID. Sanctions screening runs automatically against the name on the ID.
You can embed this flow in your own site using the JS Widget, or drive it directly via the REST API.
Authentication
All protected endpoints are authenticated via an HttpOnly JWT cookie named jwt. The cookie is set automatically when a user logs in via POST /api/login.php and is sent by the browser on every subsequent request to the same domain.
There is no Authorization header — the cookie is the credential. This means your front-end does not need to manage tokens manually for same-domain use.
Obtaining a session
POST /api/login.php
Content-Type: application/json
{
"email": "user@example.com",
"password": "yourpassword"
}
On success the server sets the jwt cookie (1-hour expiry, HttpOnly, Secure, SameSite=Lax) and returns:
{ "message": "Login successful" }
Ending a session
GET /api/logout.php
Clears the jwt cookie and redirects to /login.html.
Embeddable JS Widget
The Peabody Secure widget is a self-contained JavaScript library that renders the full ID upload + liveness + face-match flow inside a modal overlay on your page. The user must already be logged in (JWT cookie present) before you call open().
1. Include the script
<script src="https://peabodysecure.com/js/peabody-widget.js"></script>
2. Initialize and open
PeabodySecure.init({
baseUrl: 'https://peabodysecure.com',
onSuccess: function(verificationId) {
console.log('Verified! ID:', verificationId);
// Send verificationId to your backend to record the result
},
onError: function(err) {
console.error('Verification error:', err);
}
});
// Launch the verification modal
PeabodySecure.open();
jwt cookie. It will not work across origins unless the user has an active session on peabodysecure.com.
Widget options
| Option | Type | Required | Description |
|---|---|---|---|
baseUrl | string | No | Base URL of the Peabody Secure service. Defaults to the current origin. |
onSuccess | function | Yes | Called with (verificationId) when the flow completes successfully. |
onError | function | No | Called with an error message string if the flow fails. |
Auth Endpoints
All endpoints are direct PHP files — there is no URL rewriting. Use the exact paths shown below.
POST /api/register.php
Create a new account. Returns an API key you can use to identify your account.
Request body (JSON)
{
"name": "Jane Smith",
"email": "jane@example.com",
"password": "minimum8chars"
}
Response 201
{
"message": "Registration successful",
"api_key": "a3f8c2..."
}
POST /api/login.php
Authenticate and receive a session cookie.
Request body (JSON)
{
"email": "jane@example.com",
"password": "yourpassword"
}
Response 200 — sets jwt cookie
{ "message": "Login successful" }
GET /api/logout.php
Clears the session cookie and redirects to /login.html.
KYC Flow
The three-step verification flow. All endpoints require an active session (JWT cookie). Call them in order.
Step 1 — Upload ID images
POST /api/kyc/upload.php
Upload the front and back of the user's driver's license. The back barcode is parsed automatically and the extracted data is stored against the verification record.
Request — multipart/form-data
front (file) Front of ID — JPEG, PNG, or PDF, max 10 MB
back (file) Back of ID — JPEG, PNG, or PDF, max 10 MB
Response 200
{
"verification_id": "uuid-string"
}
Store verification_id — it is required by all subsequent steps.
Step 2 — Liveness check
GET /api/kyc/liveness-challenge.php
Get a random liveness challenge to present to the user.
Response 200
{ "challenge": "Blink" }
// Possible values: "Blink", "Smile", "Look Left", "Look Right"
POST /api/kyc/liveness-check.php
Submit a short video clip of the user performing the challenge. Returns a best-frame selfie for use in the face match step.
Request — multipart/form-data
video (file) WebM video, ~3 seconds
challenge (string) The challenge string returned above
Response 200
{
"live": true,
"selfie_frame": "base64-encoded-jpeg"
}
// On failure:
{ "live": false, "error": "Liveness check failed or challenge not detected." }
Pass selfie_frame as the selfie image in Step 3.
Step 3 — Face match & final result
POST /api/kyc/face.php
Compare the liveness selfie to the photo on the uploaded ID. Runs AML/sanctions screening automatically using the name parsed from the ID barcode.
Request — multipart/form-data
verification_id (string) UUID from Step 1
selfie (file) JPEG selfie image (use selfie_frame from Step 2)
is_live (string) "1" if liveness passed, "0" otherwise
Response 200
{
"status": "passed", // "passed" | "pending_review" | "failed"
"face_matched": true,
"is_live": true,
"score": 0.42, // ArcFace cosine distance — lower = more similar
"sanctions_status": "clear", // "clear" | "hit" | "skipped" | "error"
"sanctions_hit_count": 0,
"sanctions_name": "JANE SMITH" // Name screened (from ID barcode)
}
Status values
| Status | Meaning |
|---|---|
passed | Face matched, liveness confirmed, sanctions clear. |
pending_review | Face matched and liveness confirmed, but a sanctions hit was found — requires manual compliance review. |
failed | Face did not match or liveness check failed. |
Mobile Camera Handoff
If a user is on a desktop and wants to use their phone camera to photograph the ID, generate a QR code linking to a mobile session. The desktop page polls for completion.
POST /api/kyc/create-mobile-session.php
Create a 15-minute mobile session. Display the returned URL as a QR code.
Response 200
{
"session_token": "hex-string",
"mobile_url": "https://peabodysecure.com/...",
"expires_in": 900
}
GET /api/kyc/mobile-session-status.php?session_token=<token>
Poll this endpoint (e.g. every 3 seconds) from the desktop page until status is complete.
Response
// Still waiting:
{ "status": "pending" }
// Phone submitted photos:
{ "status": "complete", "verification_id": "uuid-string" }
// Session timed out:
{ "status": "expired" }
When complete, use the returned verification_id to continue from Step 2 (liveness check).
Verification Results Reference
Sanctions / AML screening
Sanctions screening runs automatically during Step 3 using the name extracted from the ID barcode. The sanctions_status field in the face match response indicates the outcome.
| Value | Meaning |
|---|---|
clear | No matches found in OFAC/EU/UK sanctions databases. |
hit | One or more potential matches found. Verification is set to pending_review for manual compliance assessment. |
skipped | Name was not available from the ID (barcode could not be parsed). |
error | Screening service error — will be flagged for manual review. |
Face match score
The score field is an ArcFace cosine distance. Lower is more similar:
- 0.00 – 0.40: High confidence match
- 0.40 – 0.65: Match (within acceptance threshold)
- Above 0.65: Not matched — face comparison failed
Pages
Key pages on the site (direct file URLs — no rewriting):
| URL | Description |
|---|---|
/index.html | Homepage |
/register.html | Create an account |
/login.html | Log in |
/verify_identityV2.php | Full identity verification flow (file upload + liveness + face match) |
/dashboard.html | Review completed verifications |
/pricing.html | Pricing |
/public/swagger.html | Interactive Swagger API explorer |