API Reference · v1

FaceLearn API Documentation

A single, versioned REST API for face enrollment, verification, attendance tracking, and analytics. All endpoints accept and return JSON.

Base URL: https://api.facelearn.io/v1

All requests require an API key. Get yours from your dashboard.

Introduction

FaceLearn.io is a face recognition server designed for training programs, schools, and institutions. It handles three things exceptionally well:

Raw images are never stored. Every face is converted to an irreversible mathematical template the moment it enters the system, and the original image is discarded in the same request.

Biological data requires consent. You are responsible for obtaining explicit, informed consent from every person whose face you enroll. See our Privacy Policy and Terms of Service.

Quickstart

Get from zero to your first verification in under 5 minutes.

  1. Create an account

    Sign up at facelearn.io/signup.php. You'll receive your API key by email — save it, we only show it once.

  2. Store your key safely

    Never commit your API key to a repository. Use an environment variable:

    .env
    FACELEARN_API_KEY=fl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  3. Enroll a face

    Send a base64-encoded image to POST /enroll. You'll get back a template ID you can use for future verifications.

  4. Verify a face

    Send a live capture to POST /match-face. The server runs liveness detection and returns a match result with a confidence score.

  5. Record attendance

    On a successful match, call POST /attendance with the person's ID to write the check-in to their record.

Authentication

Every request must include your API key in the Authorization header using the Bearer scheme:

HTTP
Authorization: Bearer fl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Alternatively, the older X-API-Key header is still supported for backward compatibility:

HTTP
X-API-Key: fl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Key format

Keys follow the pattern fl_<env>_<40 random chars>:

Keep your key secret. Treat it like a password. If it leaks, revoke it immediately from your dashboard and issue a new one. Never expose it in client-side JavaScript or mobile apps — always proxy through your own server.

Errors

FaceLearn uses conventional HTTP status codes. Responses with errors return a JSON body with a machine-readable error code and a human-readable message.

JSON
{
  "error": "invalid_api_key",
  "message": "The API key provided is invalid or has been revoked."
}
StatusMeaningCommon causes
200 Success Request processed
400 Bad request Missing fields, malformed JSON
401 Unauthorized Missing or invalid API key
403 Forbidden Key lacks the required scope
404 Not found Resource doesn't exist or belongs to another org
422 Unprocessable Face not detected, liveness failed
429 Rate limited Too many requests — see Retry-After header
500 Server error Unexpected failure — contact support

Rate limits

Rate limits are applied per API key:

PlanRequests / minRequests / day
Starter301,000
Growth300100,000
EnterpriseCustomCustom

When you exceed a limit, the API returns 429 Too Many Requests with a Retry-After header indicating the number of seconds to wait:

HTTP
HTTP/2 429
Retry-After: 42
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1735689600

Enroll a face

POST /enroll

Register a face and store it as an encrypted template. The raw image is discarded immediately after the template is created.

Request body

FieldTypeRequiredDescription
image string required Base64-encoded JPEG or PNG. Max 5 MB, min 200×200 px.
person_id string required Your internal ID for this person (e.g. a student number). Must be unique per org.
label string optional Human-readable label, e.g. "Juan Dela Cruz".
metadata object optional Any JSON object — cohort, department, enrollment date, etc.
cURL
curl -X POST https://api.facelearn.io/v1/enroll \
  -H "Authorization: Bearer $FACELEARN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "'$(base64 -w0 face.jpg)'",
    "person_id": "STU-2024-0842",
    "label": "Juan Dela Cruz",
    "metadata": { "cohort": "A", "department": "Engineering" }
  }'

Response

JSON · 200 OK
{
  "ok": true,
  "template_id": "tpl_9a8b7c6d5e4f3a2b",
  "person_id": "STU-2024-0842",
  "quality_score": 0.94,
  "created_at": "2026-10-08T13:42:11+08:00"
}

Quality matters. quality_score is a 0–1 confidence that the captured image is suitable for matching. Anything below 0.7 should be re-enrolled with a better photo (better lighting, straight-on angle, no sunglasses/masks).

Verify a face

POST /match-face

Compare a live captured face against one or more stored templates. Includes server-side liveness detection — spoofing attempts (photos, videos, masks) are rejected.

Request body

FieldTypeRequiredDescription
image string required Base64-encoded live capture. Same size limits as enroll.
person_id string optional If set, matches only against this person's templates (1:1). If omitted, searches all templates in your org (1:N).
threshold float optional Match confidence threshold, 0.0–1.0. Default 0.75. Higher = stricter.
liveness bool optional Enable liveness detection. Default true. Do not disable in production.
cURL
curl -X POST https://api.facelearn.io/v1/match-face \
  -H "Authorization: Bearer $FACELEARN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "'$(base64 -w0 capture.jpg)'",
    "person_id": "STU-2024-0842"
  }'

Response — match found

JSON · 200 OK
{
  "ok": true,
  "matched": true,
  "person_id": "STU-2024-0842",
  "confidence": 0.992,
  "liveness_passed": true,
  "processing_ms": 187
}

Response — no match

JSON · 200 OK
{
  "ok": true,
  "matched": false,
  "confidence": 0.41,
  "liveness_passed": true
}

Response — liveness failed (spoofing attempt)

JSON · 422 Unprocessable
{
  "ok": false,
  "error": "liveness_failed",
  "message": "The submitted image does not appear to be a live capture.",
  "reason": "no_depth_cues"
}

Always check both fields. A response with "matched": true but "liveness_passed": false means the face looked right but the server suspects a spoof. Never treat that as a valid identity.

Record attendance

POST /attendance

Write a check-in event to a person's academic record. Typically called right after a successful /match-face response.

Request body

FieldTypeRequiredDescription
person_id string required Who is checking in.
session_id string required Your internal session identifier.
status string optional present (default) · late · excused · absent.
occurred_at string optional ISO 8601 timestamp. Defaults to server time. Useful for offline-queued check-ins.
metadata object optional Any extra JSON to attach to the record.
cURL
curl -X POST https://api.facelearn.io/v1/attendance \
  -H "Authorization: Bearer $FACELEARN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "person_id": "STU-2024-0842",
    "session_id": "COHORT-A-2026-10-08-MORNING",
    "status": "present"
  }'

Response

JSON · 200 OK
{
  "ok": true,
  "attendance_id": "att_3f8e2a1b4c5d6e7f",
  "person_id": "STU-2024-0842",
  "session_id": "COHORT-A-2026-10-08-MORNING",
  "status": "present",
  "occurred_at": "2026-10-08T08:02:14+08:00"
}

Idempotent by design. Submitting the same person_id + session_id twice returns the original record — not a duplicate. Safe to retry on network errors.

Today's stats

GET /stats_today

Returns aggregate check-in statistics for the current day (in your org's timezone).

Query parameters

FieldTypeRequiredDescription
session_id string optional Scope to a single session. Omit for org-wide totals.
cURL
curl https://api.facelearn.io/v1/stats_today?session_id=COHORT-A-2026-10-08-MORNING \
  -H "Authorization: Bearer $FACELEARN_API_KEY"

Response

JSON · 200 OK
{
  "ok": true,
  "date": "2026-10-08",
  "session_id": "COHORT-A-2026-10-08-MORNING",
  "enrolled": 42,
  "present": 38,
  "late": 2,
  "absent": 2,
  "attendance_rate": 0.952
}

Webhooks

Register a webhook URL in your dashboard and we'll POST a signed JSON payload whenever key events occur.

EventWhen it fires
face.enrolledA new template is successfully created
face.matchedA verification succeeds above the threshold
face.spoof_detectedLiveness detection rejects a capture
attendance.recordedA check-in is written to a record

Every webhook includes an X-FaceLearn-Signature header — an HMAC-SHA256 of the raw body using your webhook secret:

PHP
// Verify webhook authenticity
$payload   = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_FACELEARN_SIGNATURE'] ?? '';
$expected  = hash_hmac('sha256', $payload, 'your_webhook_secret');

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}

$event = json_decode($payload, true);
// handle $event['type'] ...

Always verify the signature. Never trust a webhook body without checking the HMAC first — otherwise anyone can POST fake events to your endpoint.

SDKs & libraries

Official SDKs are coming soon. In the meantime, the API is plain JSON over HTTPS — any HTTP client will work.

PHP · cURL
// Enroll a face
$ch = curl_init('https://api.facelearn.io/v1/enroll');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . getenv('FACELEARN_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'image'     => base64_encode(file_get_contents('face.jpg')),
        'person_id' => 'STU-2024-0842',
    ]),
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);
JavaScript · fetch
// Enroll a face
const res = await fetch('https://api.facelearn.io/v1/enroll', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.FACELEARN_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    image: imageBase64,
    person_id: 'STU-2024-0842',
  }),
});

const data = await res.json();
Python · requests
import os, base64, requests

with open("face.jpg", "rb") as f:
    image_b64 = base64.b64encode(f.read()).decode()

res = requests.post(
    "https://api.facelearn.io/v1/enroll",
    headers={"Authorization": f"Bearer {os.environ['FACELEARN_API_KEY']}"},
    json={"image": image_b64, "person_id": "STU-2024-0842"},
)
data = res.json()

Changelog

VersionDateChanges
v1.0 Oct 2026 Initial public release — enroll, match-face, attendance, stats_today

Versioning. We follow URL-based versioning. Breaking changes ship under a new major version (/v2/). Non-breaking additions appear in the current version without notice.

Need help integrating?

We're happy to help you wire up the API. Reach out anytime.

Contact support