Sniip Identity API

Add enterprise-grade identity verification to your product in minutes. No SDK required.

Base URL: https://id.sniip.com — All API endpoints require an X-API-Key header. Get your key from the Dashboard.

Quick Start

Sniip Identity uses a secure three-step flow. Your server uploads a document, the user completes a liveness check on our hosted page, and you receive results via webhook. No biometric data or API keys ever appear in the browser.

1
Create Session Your server uploads the ID document and gets a session ID + verify URL
2
Redirect User Send the user to the verify_url for the liveness check
3
Receive Results Liveness + face comparison results delivered via signed webhook POST
bash
# The entire integration in one command
curl -X POST https://id.sniip.com/api/v1/verify/session \
  -H 'X-API-Key: si_live_abc123...' \
  -F 'reference_id=user_12345' \
  -F 'redirect_url=https://yourapp.com/done' \
  -F 'webhook_url=https://yourapp.com/webhook' \
  -F 'document=@drivers_licence.jpg'

# Response:
{
  "session_id":  "9c77d4c1035102ce25556972d6d1392b",
  "verify_url":  "https://id.sniip.com/verify?token=a1b2c3..."
}

Authentication

All API requests require an X-API-Key header. Keys are generated from the Dashboard and scoped to your tenant account.

http header
X-API-Key: si_live_a1b2c3d4e5f6g7h8i9j0...

API keys start with si_live_ followed by 32 random characters. Store keys securely and never expose them in client-side code or public repositories.

Keep your key secret. API keys authenticate server-to-server requests. Never include them in frontend JavaScript, mobile apps, or version control.

Verification Flow

The verify flow is the primary integration pattern. It handles the complete identity verification lifecycle: document upload, liveness detection, face comparison, and result delivery.

How It Works

  1. Your server creates a session by uploading the ID document with your API key. This returns a unique session_id and a ready-to-use verify_url.
  2. You redirect the user to the verify_url. The URL contains only an opaque token — no API key, no document URL, no sensitive data.
  3. The user completes a liveness check on our hosted page (camera-based, works on any modern browser).
  4. Sniip Identity compares the live selfie against the uploaded document and determines face match and liveness.
  5. Results are delivered two ways: the user is redirected back to your redirect_url, and full results are POSTed to your webhook_url.

Create Verification Session

POST /api/v1/verify/session

Creates a new verification session. Upload the reference document (government ID photo) and specify where to send results. The response contains a session_id (for tracking) and a verify_url (for redirecting the user).

Request Parameters

Send as multipart/form-data:

X-API-Key Header Your Sniip Identity API key
reference_id Required Your unique identifier for this verification (e.g., user ID, application ID). Returned in webhook.
redirect_url Required Where to redirect the user after verification completes
webhook_url Required URL to receive the full verification result via HTTP POST
document Required The ID document image file (JPEG or PNG, max 10MB)

Response

json — 200 OK
{
  "session_id":  "9c77d4c1035102ce25556972d6d1392b",
  "verify_url":  "https://id.sniip.com/verify?token=a1b2c3d4e5f6..."
}
FieldTypeDescription
session_idstringUnique 32-character hex identifier for this verification session. Use this to query results and correlate webhooks.
verify_urlstringAbsolute URL to redirect the user to for the liveness check. Ready to use — no base URL prefix needed.

Redirect the User

Redirect the user to the verify_url returned by the Create Session endpoint. The URL is absolute and ready to use — no assembly required:

url
https://id.sniip.com/verify?token=a1b2c3d4e5f6789012345678abcdef01

The user completes a liveness check on our hosted page. After completion:

Redirect Query Parameters

redirect url
https://yourapp.com/done?session_id=a1b2c3d4

Verification results (similarity scores, match status, liveness confidence) are never exposed in the redirect URL or to the end user’s browser. Use the webhook payload or call the API with the session_id to retrieve results.

Webhook Results

After verification completes, we POST the full result to your webhook_url. This is the authoritative source of truth — always use webhook data for business decisions.

POST your webhook_url
json — webhook payload
{
  "event":        "verification.completed",
  "session_id":   "9c77d4c1035102ce25556972d6d1392b",
  "reference_id": "user_12345",
  "status":       "completed",
  "timestamp":    "2026-02-14T12:00:00Z",
  "result": {
    "liveness":            true,
    "liveness_confidence": 98.7,
    "liveness_status":     "Succeeded",
    "face_match":          true,
    "face_confidence":     96.4,
    "face_message":        "Face match found with 96.4% similarity"
  }
}

Webhook Headers

HeaderDescription
Content-Typeapplication/json
X-Sniip-SignatureHMAC-SHA256 signature of the request body for verifying authenticity
X-Sniip-EventEvent type — always verification.completed

Result Fields

FieldTypeDescription
eventstringAlways verification.completed
session_idstring32-character hex session identifier (matches the session_id from Create Session)
reference_idstringThe reference ID you provided when creating the session
result.livenessbooleantrue if the person is live (not a spoof, mask, or deepfake)
result.liveness_confidencenumberLiveness confidence score (0–100)
result.face_matchbooleantrue if the live selfie matches the uploaded document
result.face_confidencenumberFace similarity score (0–100). Above 80 indicates a match.
result.face_messagestringHuman-readable result description
Webhook Retries: If your endpoint returns a non-2xx status, we retry up to 3 times with exponential backoff (5s, 30s, 120s).

GET /liveness/results

GET /api/v1/liveness/results?session_id={id}

Retrieve liveness detection results for a session. Returns the liveness status, confidence score, and optionally the reference image captured during the check.

session_id Required The liveness session ID returned when the session was created
json — 200 OK
{
  "is_live":              true,
  "confidence":           98.7,
  "status":               "Succeeded",
  "has_reference_image":  true,
  "reference_image_base64": "/9j/4AAQSkZJRg..."
}

Code Examples

Complete integration examples for popular languages. Each example shows the full flow: create session, redirect user, and handle webhook.

node.js / express
const fetch = require('node-fetch');
const FormData = require('form-data');
const fs = require('fs');
const crypto = require('crypto');

// Step 1: Create session and redirect user
app.get('/verify-user', async (req, res) => {
  const form = new FormData();
  form.append('reference_id', req.user.id);
  form.append('redirect_url', 'https://yourapp.com/done');
  form.append('webhook_url', 'https://yourapp.com/api/webhook');
  form.append('document', fs.createReadStream(req.user.idPhotoPath));

  const resp = await fetch('https://id.sniip.com/api/v1/verify/session', {
    method: 'POST',
    headers: { 'X-API-Key': process.env.SNIIP_API_KEY },
    body: form,
  });
  const { session_id, verify_url } = await resp.json();
  console.log('Session created:', session_id);
  res.redirect(verify_url);
});

// Step 2: Handle webhook results
app.post('/api/webhook', (req, res) => {
  // Verify signature
  const sig = req.headers['x-sniip-signature'];
  const expected = crypto
    .createHmac('sha256', process.env.SNIIP_API_KEY)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (sig !== expected) return res.status(401).send('Invalid signature');

  const { reference_id, result } = req.body;
  console.log('Verified:', reference_id,
    'live:', result.liveness,
    'match:', result.face_match,
    'confidence:', result.face_confidence);

  // Update your user record
  res.sendStatus(200);
});
python / flask
import os, hmac, hashlib, requests
from flask import Flask, redirect, request, jsonify

app = Flask(__name__)
API_KEY = os.environ['SNIIP_API_KEY']

# Step 1: Create session and redirect
@app.route('/verify-user')
def start_verify():
    resp = requests.post(
        'https://id.sniip.com/api/v1/verify/session',
        headers={'X-API-Key': API_KEY},
        data={
            'reference_id':  'user_456',
            'redirect_url':  'https://yourapp.com/done',
            'webhook_url':   'https://yourapp.com/hook',
        },
        files={'document': open('id_photo.jpg', 'rb')},
    )
    data = resp.json()
    print('Session:', data['session_id'])
    return redirect(data['verify_url'])

# Step 2: Handle webhook
@app.route('/hook', methods=['POST'])
def webhook():
    # Verify HMAC signature
    sig = request.headers.get('X-Sniip-Signature', '')
    expected = hmac.new(
        API_KEY.encode(), request.data, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(sig, expected):
        return 'Invalid signature', 401

    data = request.json
    print('Verified:', data['reference_id'],
          'match:', data['result']['face_match'])
    return '', 200
php
// create-session.php - Step 1
$apiKey = getenv('SNIIP_API_KEY');
$ch = curl_init('https://id.sniip.com/api/v1/verify/session');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['X-API-Key: ' . $apiKey],
    CURLOPT_POSTFIELDS     => [
        'reference_id' => $userId,
        'redirect_url' => 'https://yoursite.com/done',
        'webhook_url'  => 'https://yoursite.com/webhook.php',
        'document'     => new CURLFile('id_photo.jpg'),
    ],
]);
$result = json_decode(curl_exec($ch), true);
error_log('Session: ' . $result['session_id']);
header('Location: ' . $result['verify_url']);
exit;

// webhook.php - Step 2
$body    = file_get_contents('php://input');
$sig     = $_SERVER['HTTP_X_SNIIP_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $body, $apiKey);

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

$data  = json_decode($body, true);
$match = $data['result']['face_match'];
http_response_code(200);
go
package main

import (
    "bytes"
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

// Step 1: Create session
func createSession(refID, docPath string) (string, error) {
    body := &bytes.Buffer{}
    w := multipart.NewWriter(body)
    w.WriteField("reference_id", refID)
    w.WriteField("redirect_url", "https://yourapp.com/done")
    w.WriteField("webhook_url", "https://yourapp.com/hook")

    fw, _ := w.CreateFormFile("document", "id.jpg")
    f, _ := os.Open(docPath)
    io.Copy(fw, f)
    f.Close()
    w.Close()

    req, _ := http.NewRequest("POST",
        "https://id.sniip.com/api/v1/verify/session", body)
    req.Header.Set("X-API-Key", os.Getenv("SNIIP_API_KEY"))
    req.Header.Set("Content-Type", w.FormDataContentType())

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    var result struct{
        SessionID string `json:"session_id"`
        VerifyURL string `json:"verify_url"`
    }
    json.NewDecoder(resp.Body).Decode(&result)
    // result.SessionID = "9c77d4c1..." (use for tracking)
    return result.VerifyURL, nil
}

// Step 2: Webhook handler
func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    sig := r.Header.Get("X-Sniip-Signature")

    mac := hmac.New(sha256.New, []byte(os.Getenv("SNIIP_API_KEY")))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))

    if !hmac.Equal([]byte(sig), []byte(expected)) {
        http.Error(w, "Invalid signature", 401)
        return
    }

    var payload map[string]interface{}
    json.Unmarshal(body, &payload)
    w.WriteHeader(200)
}

Webhook Security

Every webhook payload is signed with an HMAC-SHA256 hash using your API key as the secret. Always verify the X-Sniip-Signature header before processing webhook data.

Verification Steps

  1. Read the raw request body as bytes (before JSON parsing)
  2. Compute the HMAC-SHA256 of the body using your API key as the secret
  3. Compare the computed hex digest to the X-Sniip-Signature header value
  4. Reject the request if they do not match
Use constant-time comparison. Always use hmac.compare_digest (Python), crypto.timingSafeEqual (Node), or hmac.Equal (Go) to prevent timing attacks.

Error Handling

The API uses standard HTTP status codes. Error responses include a JSON body with an error field.

StatusMeaningCommon Cause
400Bad RequestMissing required fields, invalid file format
401UnauthorizedMissing or invalid API key
403ForbiddenTenant account deactivated or feature not available on plan
404Not FoundInvalid session ID or verification not found
409ConflictSession already processed (duplicate callback)
429Too Many RequestsRate limit exceeded or Free plan verification quota reached
500Server ErrorInternal error — contact support if persistent
json — error response
{
  "error": "reference_id is required"
}

Rate Limits

Rate limits are applied per API key and vary by plan:

PlanRequests / SecondVerifications / MonthOverage
Free5100 (hard cap)Blocked at limit
Starter201,000$0.15 / verification
Pro10010,000$0.10 / verification
EnterpriseCustomCustomCustom

When rate limited, the API returns 429 Too Many Requests with a Retry-After header indicating seconds to wait.

Need help integrating? Contact us at support@sniip.com — we are happy to help you get set up.