API Reference

The Xident API allows you to verify ages, manage users, and integrate advanced features. All endpoints are served from api.xident.io under versioned path prefixes.

Base URL

https://api.xident.io

Verification widget endpoints use the /verify/v1/ prefix. Dashboard and admin endpoints use their own prefixes.

Authentication

Xident uses a Stripe-style dual key model. All API requests require the X-API-Key header:

Public Key vs Secret Key

Key Type Prefix Use In Can Access
Public Key pk_live_ / pk_test_ Browser (JS SDK) Widget-facing endpoints: requirements, liveness, OCR, feedback
Secret Key sk_live_ / sk_test_ Server only (Node, PHP, Go, Python) All endpoints, including POST /verify/v1/init and GET /verify/v1/status/:token

Public Key (frontend)

X-API-Key: pk_live_your_public_key

Secret Key (backend)

X-API-Key: sk_live_your_secret_key

Never expose your secret key in client-side code. The public key is safe to embed in your HTML or JavaScript.

Response Envelope

All API responses use a consistent envelope format:

{
  "success": true,
  "data": { ... },
  "meta": {
    "request_id": "req_xyz789",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Error responses set success: false and include an error object instead of data.

Verification Endpoints

Initialize Session

Create a new verification session. Requires secret key.

POST /verify/v1/init

Request Body

{
  "callback_url": "https://yoursite.com/verified",
  "metadata": {
    "user_id": "your-user-123"
  }
}

Response

{
  "success": true,
  "data": {
    "session_id": "ses_abc123",
    "verify_url": "https://verify.xident.io/ses_abc123"
  },
  "meta": {
    "request_id": "req_xyz789",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Get Verification Status

Retrieve the result of a completed verification. Requires secret key.

GET /verify/v1/status/:token

Response

{
  "success": true,
  "data": {
    "verified": true,
    "age_threshold": 18,
    "above_threshold": true,
    "country": "US",
    "xident_id": "xid_abc123",
    "methods_completed": ["liveness", "age_estimation"],
    "created_at": "2026-03-30T10:30:00Z",
    "metadata": {
      "user_id": "your-user-123"
    }
  },
  "meta": {
    "request_id": "req_xyz789",
    "timestamp": "2026-03-30T10:30:05Z"
  }
}

Response Fields

Field Type Description
verified boolean Whether verification was successful
age_threshold number The age threshold that was checked (e.g. 18)
above_threshold boolean Whether the user is above the required age threshold
country string ISO country code
xident_id string User's Xident ID (if they have one)
methods_completed array Verification methods used
metadata object Custom data passed in init()

Check Requirements

Get verification requirements for your project. Uses public key.

GET /verify/v1/requirements/check

Response

{
  "success": true,
  "data": {
    "requirements": {
      "min_age": 18,
      "liveness_required": true,
      "document_required": false,
      "wallet_supported": true
    },
    "project_name": "My Website"
  },
  "meta": {
    "request_id": "req_abc456",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Feedback

Submit Widget Feedback

Allow users to submit feedback about the verification experience. Uses public key.

POST /verify/v1/feedback

Request Body

{
  "session_id": "ses_abc123",
  "rating": 4,
  "comment": "Quick and easy verification process."
}

Response

{
  "success": true,
  "data": {
    "id": "fb_abc123",
    "created_at": "2026-03-30T10:35:00Z"
  },
  "meta": {
    "request_id": "req_def789",
    "timestamp": "2026-03-30T10:35:00Z"
  }
}

Request Human Review

Allow users to request a human review after a failed verification. Uses public key.

POST /verify/v1/review-request

Request Body

{
  "session_id": "ses_abc123",
  "reason": "Age was incorrectly estimated"
}

Response

{
  "success": true,
  "data": {
    "review_id": "rev_abc123",
    "status": "pending",
    "created_at": "2026-03-30T10:40:00Z"
  },
  "meta": {
    "request_id": "req_ghi012",
    "timestamp": "2026-03-30T10:40:00Z"
  }
}

EU Wallet (Path E)

Verify age using EU Digital Identity Wallet credentials (mso_mdoc attestations). Uses the W3C Digital Credentials API and OpenID4VP protocol.

Validate Wallet Attestation

Validate an mso_mdoc age attestation directly. Uses public key.

POST /verify/v1/wallet/validate

Request Body

{
  "mso_mdoc": "base64_encoded_mso_mdoc_attestation...",
  "age_threshold": 18
}

Response

{
  "success": true,
  "data": {
    "above_threshold": true,
    "age_threshold": 18,
    "issuer": "DE",
    "wallet_provider": "eu-id-wallet"
  },
  "meta": {
    "request_id": "req_wal123",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Start Wallet Transaction

Start an OpenID4VP cross-device verification flow. Returns a QR code URL for the user to scan with their wallet app. Uses public key.

POST /verify/v1/wallet/transaction

Request Body

{
  "age_threshold": 18,
  "callback_url": "https://yoursite.com/wallet-verified"
}

Response

{
  "success": true,
  "data": {
    "transaction_id": "wtx_abc123",
    "qr_code_url": "https://api.xident.io/verify/v1/wallet/transaction/wtx_abc123/qr",
    "openid4vp_url": "openid4vp://authorize?request_uri=...",
    "expires_at": "2026-03-30T10:35:00Z"
  },
  "meta": {
    "request_id": "req_wal456",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Poll Wallet Transaction Status

Poll for the status of a cross-device wallet transaction. Uses public key.

GET /verify/v1/wallet/transaction/:id/status

Response

{
  "success": true,
  "data": {
    "transaction_id": "wtx_abc123",
    "status": "completed",
    "above_threshold": true,
    "age_threshold": 18
  },
  "meta": {
    "request_id": "req_wal789",
    "timestamp": "2026-03-30T10:32:00Z"
  }
}

Face 2FA

Register Face

Enroll a user's face for 2FA on your site.

POST /verify/v1/face-2fa/register

Request Body

{
  "user_id": "your-user-123",
  "face_image": "base64_encoded_image..."
}

Response

{
  "success": true,
  "data": {
    "face_id": "face_abc123",
    "enrolled_at": "2026-03-30T10:30:00Z"
  },
  "meta": {
    "request_id": "req_f2a123",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Verify Face

Authenticate a user via face match.

POST /verify/v1/face-2fa/verify

Request Body

{
  "user_id": "your-user-123",
  "face_image": "base64_encoded_image..."
}

Response

{
  "success": true,
  "data": {
    "verified": true,
    "confidence": 0.97,
    "face_id": "face_abc123"
  },
  "meta": {
    "request_id": "req_f2a456",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Blacklist

Check Blacklist

Check if a face matches any in your blacklist.

POST /verify/v1/blacklist/check

Request Body

{
  "face_image": "base64_encoded_image..."
}

Response (No Match)

{
  "success": true,
  "data": {
    "match_found": false,
    "matches": []
  },
  "meta": {
    "request_id": "req_bl123",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Response (Match Found)

{
  "success": true,
  "data": {
    "match_found": true,
    "matches": [
      {
        "blacklist_id": "bl_abc123",
        "similarity": 0.94,
        "reason": "fraud",
        "added_at": "2026-01-10T08:00:00Z"
      }
    ]
  },
  "meta": {
    "request_id": "req_bl456",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Add to Blacklist

Add a face to your blacklist.

POST /verify/v1/blacklist/add

Request Body

{
  "face_image": "base64_encoded_image...",
  "reason": "fraud",
  "metadata": {
    "original_user_id": "user-456"
  }
}

Response

{
  "success": true,
  "data": {
    "blacklist_id": "bl_abc123"
  },
  "meta": {
    "request_id": "req_bl789",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Usage

Get Usage Statistics

Retrieve usage data for your project.

GET /dashboard/v1/usage

Query Parameters

Parameter Type Description
start_date string Start date (ISO 8601)
end_date string End date (ISO 8601)

Response

{
  "success": true,
  "data": {
    "period": {
      "start": "2026-03-01T00:00:00Z",
      "end": "2026-03-31T23:59:59Z"
    },
    "summary": {
      "total_verifications": 1523,
      "full_verifications": 892,
      "token_verifications": 631,
      "total_cost": 45.67
    },
    "by_type": [
      {
        "type": "full",
        "count": 892,
        "cost": 35.68
      },
      {
        "type": "token",
        "count": 631,
        "cost": 9.47
      }
    ]
  },
  "meta": {
    "request_id": "req_usg123",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Error Responses

All errors follow this format:

{
  "success": false,
  "error": {
    "code": "invalid_token",
    "message": "The token has expired or already been used"
  },
  "meta": {
    "request_id": "req_err123",
    "timestamp": "2026-03-30T10:30:00Z"
  }
}

Error Codes

Code HTTP Status Description
invalid_token 400 Token is invalid, expired, or already used
unauthorized 401 Invalid or missing API key
forbidden 403 API key doesn't have permission
not_found 404 Resource not found
rate_limited 429 Too many requests
internal_error 500 Server error

Rate Limits

Endpoint Limit
/verify/v1/init 100 requests/minute
/verify/v1/status/* 100 requests/minute
/verify/v1/face-2fa/* 60 requests/minute
/verify/v1/blacklist/* 30 requests/minute
/verify/v1/wallet/* 60 requests/minute
/verify/v1/feedback 10 requests/minute
/verify/v1/review-request 10 requests/minute
/dashboard/v1/usage 10 requests/minute

Rate limit headers are included in responses:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705315200

Webhooks

Configure webhooks in your Dashboard to receive real-time events.

Event Types

Event Description
verification.completed User completed verification
verification.failed Verification failed
token.verified Token was verified

Webhook Payload

{
  "id": "evt_abc123",
  "type": "verification.completed",
  "created_at": "2026-03-30T10:30:00Z",
  "data": {
    "verification_id": "ver_xyz789",
    "user_id": "your-user-123",
    "above_threshold": true,
    "age_threshold": 18,
    "methods_completed": ["liveness", "age_estimation"]
  }
}

Signature Verification

Verify webhook signatures to ensure authenticity:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Next Steps