Introduction
Recspace exposes three core services — OCR extraction, face matching with liveness, and PII masking — as stateless REST endpoints. Every request is idempotent when supplied with an Idempotency-Key header, and every response ships with a request_id for observability.
Conventions
All endpoints return JSON. All timestamps are ISO 8601 UTC. All file uploads are multipart/form-data, with a per-file limit of 20 MB. Request IDs are returned in the X-Request-Id header and in the body.
Authentication
All requests require a bearer token in the Authorization header. Tokens are issued per environment (sandbox, production) and are never time-limited — rotate them via the dashboard.
Authorization: Bearer rsk_live_8B3A2Fc9d4e7a01b Content-Type: application/json
Rate limits
Default limits scale with your plan. Exceeding them returns 429 Too Many Requests with a Retry-After header. We recommend exponential backoff with jitter for automated retry.
| Plan | RPM | Concurrent | Burst |
|---|---|---|---|
| Sandbox | 60 | 10 | 100 |
| Starter | 300 | 50 | 500 |
| Scale | 1200 | 200 | 2000 |
| Enterprise | Custom | Custom | Custom |
OCR · Extract fields
Extract structured fields from a document image. Auto-detects document type if not specified.
Parameters
| Name | Description |
|---|---|
| document | Image file (JPEG, PNG, PDF). Max 20 MB. filerequired |
| type | ID type hint — aadhaar, pan, passport, dl, voter_id. string |
| mask_pii | Mask PII fields in response (default: false). boolean |
| return_bbox | Include field bounding boxes (default: false). boolean |
Example request
curl -X POST https://api.recspace.in/v1/ocr/extract \ -H "Authorization: Bearer $API_KEY" \ -F "document=@aadhaar.jpg" \ -F "type=aadhaar"
OCR · Response schema
Every OCR response includes a top-level confidence and a per-field confidence inside each field object.
{
"request_id": "ocr_8B3A2F",
"document_type": "aadhaar_front",
"confidence": 0.994,
"verhoeff_valid": true,
"fields": {
"name": { "value": "Priya Sharma", "confidence": 0.998 },
"dob": { "value": "1992-03-14", "confidence": 0.992 },
"aadhaar_number": { "value": "XXXX XXXX 9012", "confidence": 0.997 }
},
"processing_time_ms": 680
}
Face · Match
Compare two faces and return a similarity score. Optionally run liveness detection on the selfie in the same call.
Parameters
| Name | Description |
|---|---|
| id_photo | Reference face — from an ID card or stored photo. filerequired |
| selfie | Live face capture. filerequired |
| check_liveness | Run passive liveness (default: true). boolean |
| threshold | Similarity threshold, 0.0 to 1.0 (default: 0.80). float |
Face · Liveness only
Run only liveness detection on a face image — no match. Useful when matching happens in a later step.
Masking · Apply
Redact PII fields in a JSON payload. Choose reversible (vault-stored) or irreversible (destructive) mode.
Parameters
| Name | Description |
|---|---|
| payload | The JSON object to mask. objectrequired |
| mask_fields | Fields to mask, dot-path notation (e.g. user.aadhaar). array |
| mask_mode | reversible or irreversible. string |
| purpose | Purpose tag — logged in the audit trail. string |
| retention_days | For reversible mode — vault expiry (max 365). int |
Masking · Unmask
Recover original values for a reversible-masked payload. Requires an X-Role header and an X-Reason header — both are logged to the audit ledger.
Webhooks · Event payloads
For long-running verifications (V-CIP sessions, batch OCR), events are delivered to your configured webhook endpoint. Each event is a signed JSON payload sent as an HTTP POST.
Event types
| Event | Description |
|---|---|
| kyc.completed | Full KYC session finished successfully |
| kyc.failed | Session failed — reason in payload |
| vcip.recorded | V-CIP video session recorded and stored |
| masking.unmasked | A reversible mask was decrypted (audit event) |
Example payload
{
"event": "kyc.completed",
"session_id": "kyc_8B3A2F",
"customer_id": "cust_19482",
"completed_at": "2026-04-20T14:32:18Z",
"result": "verified",
"confidence": 0.994
}
Webhooks · Signing
Every webhook request carries an X-Recspace-Signature header — an HMAC-SHA256 of the raw body with your webhook secret. Verify before trusting payloads.
const crypto = require('crypto'); const sig = req.headers['x-recspace-signature']; const expected = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(req.rawBody) .digest('hex'); if (sig !== expected) return res.status(400).end();
Error codes
All errors return a JSON body with error, code, and request_id fields. Retry 5xx errors with exponential backoff; 4xx errors require fixing the input.
| Status | Meaning |
|---|---|
| 400 · bad_request | Malformed request — missing or invalid params |
| 401 · unauthorized | Missing, invalid, or expired bearer token |
| 403 · forbidden | Token valid but lacks scope for this endpoint |
| 404 · not_found | Resource (session, mask ID) not found |
| 413 · payload_too_large | File size exceeds 20 MB limit |
| 422 · document_unreadable | OCR confidence below minimum — ask for recapture |
| 429 · rate_limited | Rate limit exceeded — see Retry-After |
| 500 · internal_error | Server-side failure — safe to retry |
| 503 · service_unavailable | Temporary outage — retry with backoff |
Changelog
v3.2 — April 2026
Added 1:N face search endpoint (/v1/face/search), extended Aadhaar OCR confidence scoring to include regional-language variants, deepfake injection-attack detection for V-CIP.
v3.1 — January 2026
DPDP Act 2023 compliance mode, signed audit export, webhook retry with exponential backoff.
v3.0 — September 2025
Major revision. New OCR model (+2.1pp accuracy), passive-only liveness, reversible masking with KMS integration.
Need API keys?
Sandbox access is self-serve. Production access requires a quick compliance review — typically one business day.