Overview
All recognition endpoints live under /api/v1/ and require:
Authorization: Bearer vf_test_<your-secret>
Test keys use the vf_test_ prefix; production keys use vf_live_. The plain secret is shown once when you create a key.
Responses use a consistent envelope:
{"success": true, "data": { ... }}
{"success": false, "error": {"code": "...", "message": "..."}}
Image uploads use multipart/form-data with a JPEG or PNG file in the image field.
Default match threshold: 0.4 cosine similarity (tunable per verify/identify request).
Rate limits apply per API key. See error codes for 429 responses.
1. Register
Fastest path: open /register/, create an account, and sign in.
Your personal organization is created automatically. Note its ID from the dashboard or the register API response.
curl (session API)
Session POST requests need a CSRF token. Fetch one from the login page first:
BASE="https://verifai.iqtbots.ai"
CSRF=$(curl -s -c /tmp/verifai.cookies "$BASE/login/" | grep -o 'csrfmiddlewaretoken" value="[^"]*"' | cut -d'"' -f4)
curl -s -b /tmp/verifai.cookies -c /tmp/verifai.cookies \
-H "X-CSRFToken: $CSRF" -H "Content-Type: application/json" \
-X POST "$BASE/api/v1/auth/register/" \
-d '{
"email": "dev@example.com",
"password": "securepass123",
"password_confirm": "securepass123",
"first_name": "Alex"
}'
Save data.personal_organization.id from the response as ORG_ID.
2. Create API key
Dashboard: go to your organization → Create API key. Copy the secret immediately.
curl (session API)
ORG_ID="<your-organization-uuid>"
curl -s -b /tmp/verifai.cookies \
-H "X-CSRFToken: $CSRF" -H "Content-Type: application/json" \
-X POST "$BASE/api/v1/dashboard/organizations/$ORG_ID/api-keys/" \
-d '{"name": "My app (dev)", "environment": "test"}'
Save data.secret as API_KEY. Confirm it works:
curl -s -H "Authorization: Bearer $API_KEY" "$BASE/api/v1/auth/verify/"
3. Create a collection
Collections are tenant-scoped galleries that group enrolled faces.
curl -s -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-X POST "$BASE/api/v1/collections/" \
-d '{"name": "Employees", "description": "Staff access control"}'
Save data.collection.id as COLLECTION_ID.
4. Enroll a face
Upload a clear front-facing photo. Use your own external_id (e.g. employee number) to reference the person later.
curl -s -H "Authorization: Bearer $API_KEY" \
-X POST "$BASE/api/v1/collections/$COLLECTION_ID/persons/" \
-F "external_id=employee_4821" \
-F "display_name=Alex Morgan" \
-F 'metadata={"department":"Engineering"}' \
-F "image=@/path/to/face.jpg"
On success you receive the person record plus enrollment metadata (model, confidence, bounding box).
5. Verify (1:1)
Compare a new photo against one enrolled person. This is the primary MVP flow for login and check-in.
curl -s -H "Authorization: Bearer $API_KEY" \
-X POST "$BASE/api/v1/verify/" \
-F "collection_id=$COLLECTION_ID" \
-F "external_id=employee_4821" \
-F "threshold=0.4" \
-F "image=@/path/to/probe.jpg"
Example success payload:
{
"success": true,
"data": {
"matched": true,
"similarity": 0.91,
"threshold": 0.4,
"person": { "id": "...", "external_id": "employee_4821", ... },
"probe": { "confidence": 0.88, "bbox": [x1, y1, x2, y2] }
}
}
6. Identify (1:N) (small galleries)
Search an entire collection for the best matches above the threshold.
MVP uses a linear O(n) scan — suitable for modest watchlists (default max
500 enrolled faces). For login and check-in when you know the user,
use verify (1:1) instead. Large-scale 1:N (thousands of faces) needs a vector
index (planned); oversized collections return gallery_too_large.
curl -s -H "Authorization: Bearer $API_KEY" \
-X POST "$BASE/api/v1/identify/" \
-F "collection_id=$COLLECTION_ID" \
-F "threshold=0.4" \
-F "max_results=5" \
-F "image=@/path/to/probe.jpg"
Responses include gallery_size and search_method: "linear_scan".
Error codes
Recognition endpoints return structured errors in the error.code field. Auth failures use DRF's detail field.
| HTTP |
Code |
When |
| 401 | — | Invalid or revoked API key ({"detail": "Invalid or revoked API key."}) |
| 403 | — | Missing API key on a protected endpoint |
| 429 | rate_limit_exceeded | Per-minute burst limit exceeded for this API key |
| 429 | daily_quota_exceeded | Daily request cap reached (resets at UTC midnight) |
| 400 | validation_error | Invalid JSON body or serializer validation |
| 400 | missing_image | No image file in multipart request |
| 400 | missing_external_id | Enroll without external_id |
| 400 | missing_collection_id | Verify/identify without collection_id |
| 400 | missing_person_reference | Verify without person_id or external_id |
| 400 | invalid_metadata | metadata is not valid JSON object |
| 400 | invalid_image | File is not a decodable JPEG/PNG |
| 400 | invalid_threshold | Threshold is not a number |
| 400 | invalid_max_results | max_results is not a positive integer |
| 404 | person_not_found | Person ID or external_id not in collection |
| 404 | not_enrolled | Person exists but has no enrolled face |
| 422 | image_quality_failed | Enroll with strict_quality=true and quality checks failed |
| 422 | no_face_detected | Valid image but no face found |
| 422 | gallery_too_large | Identify on a collection above the configured gallery cap — use verify or split collections |
| 500 | face_processing_error | InsightFace pipeline failure |