Getting Started with Xident
Xident is a backend-first integration: your server creates a verification session, sends the user to the Xident widget, and reads the result back — all with your secret key. No frontend SDK required.
How it works
- Your backend calls
POST /verify/v1/initand gets a one-timeverify_url. - You redirect the user to that
verify_url(the Xident widget). - The user verifies (liveness, age check, and/or document capture).
- Xident redirects back to your
callback_urlwith a result token. - Your backend calls
GET /verify/v1/result/{token}to read the outcome.
Prerequisites
- A Xident account (sign up here)
- A project with a secret API key (
sk_live_…) - A callback URL on your domain (HTTPS required in production;
http://localhostallowed in development)
Step 1: Get your secret key
- Log in to dashboard.xident.io
- Click "Create Project" and enter your domain
- Copy your secret key (
sk_live_…) and store it as a server-side environment variable — never expose it in client-side code
One key for the whole flow
The secret key (sk_live_) authenticates both POST /verify/v1/init and GET /verify/v1/result/{token} via the X-API-Key header. A publishable key (pk_live_) also exists for optional client-side widget embedding (see Core Concepts), but it is not needed for the standard backend integration.
Step 2: Create a verification session (backend)
When the user starts verification, your backend calls POST /verify/v1/init and redirects them to the returned verify_url:
Node.js
// Backend route: create a verification session with your SECRET key,
// then send the user to the returned verify_url.
app.get('/start-verification', async (req, res) => {
const response = await fetch('https://api.xident.io/verify/v1/init', {
method: 'POST',
headers: {
'X-API-Key': process.env.XIDENT_SECRET_KEY, // sk_live_...
'Content-Type': 'application/json',
},
body: JSON.stringify({
callback_url: 'https://yoursite.com/verified',
min_age: 18,
user_id: req.user?.id, // optional — echoed back on the callback
}),
});
const { data } = await response.json();
// data.verify_url = https://verify.xident.io?t=xit_...
res.redirect(data.verify_url);
});
Python
# Backend route: create a verification session with your SECRET key,
# then send the user to the returned verify_url.
@app.route('/start-verification')
def start_verification():
response = requests.post(
'https://api.xident.io/verify/v1/init',
headers={'X-API-Key': os.environ['XIDENT_SECRET_KEY']}, # sk_live_...
json={
'callback_url': 'https://yoursite.com/verified',
'min_age': 18,
'user_id': current_user.id, # optional — echoed back on the callback
},
)
data = response.json()['data']
# data['verify_url'] = https://verify.xident.io?t=xit_...
return redirect(data['verify_url'])
curl
curl https://api.xident.io/verify/v1/init \
-X POST \
-H "X-API-Key: sk_live_your_secret_key" \
-H "Content-Type: application/json" \
-d '{"callback_url": "https://yoursite.com/verified", "min_age": 18}'
The response contains the one-time init token and the URL to send the user to:
{
"success": true,
"data": {
"token": "xit_9f8e7d6c5b4a39281706f5e4d3c2b1a0",
"verify_url": "https://verify.xident.io?t=xit_9f8e7d6c5b4a39281706f5e4d3c2b1a0"
}
}
Step 3: The user verifies
After you redirect the user to verify_url, they complete verification on verify.xident.io. You don't build any UI — the widget handles liveness, age estimation, and document capture as required.
Step 4: Handle the callback
When verification finishes, Xident redirects the user back to your callback_url with query parameters:
https://yoursite.com/verified?token=xtk_abc123&status=success
The token here is the result token (xtk_…), which is different from the one-time init token (xit_…) carried in the verification URL as ?t=. Pass this xtk_ token to the result endpoint in Step 5.
Callback parameters
| Parameter | Description |
|---|---|
token | Result token (xtk_…) — pass to GET /verify/v1/result/{token} |
status | success, failed, or cancelled |
user_id | Your user_id if you provided one at init |
Step 5: Fetch the result (backend)
Always read the result server-side with your secret key — never trust the callback status alone.
Node.js
app.get('/verified', async (req, res) => {
// token is the RESULT token (xtk_...) returned on the callback.
const { token, status } = req.query;
if (status === 'cancelled') return res.redirect('/verification-cancelled');
if (status !== 'success') return res.redirect('/verification-failed'); // status === 'failed'
try {
const response = await fetch(
'https://api.xident.io/verify/v1/result/' + token,
{ headers: { 'X-API-Key': process.env.XIDENT_SECRET_KEY } } // sk_live_...
);
const { success, data } = await response.json();
// data.status is the authoritative outcome: 'completed' (passed) | 'failed' | 'canceled'
if (success && data.status === 'completed') {
req.session.ageVerified = true;
return res.redirect('/content');
}
} catch (error) {
console.error('Verification error:', error);
}
return res.redirect('/verification-failed');
});
Python
@app.route('/verified')
def handle_verification():
token = request.args.get('token') # RESULT token (xtk_...) from the callback
status = request.args.get('status')
if status == 'cancelled':
return redirect('/verification-cancelled')
if status != 'success': # status == 'failed'
return redirect('/verification-failed')
response = requests.get(
f'https://api.xident.io/verify/v1/result/{token}',
headers={'X-API-Key': os.environ['XIDENT_SECRET_KEY']}, # sk_live_...
)
body = response.json()
# data['status'] is the authoritative outcome: 'completed' | 'failed' | 'canceled'
if body.get('success') and body['data'].get('status') == 'completed':
session['age_verified'] = True
return redirect('/content')
return redirect('/verification-failed')
Verification response
The result endpoint returns the Xident API envelope:
{
"success": true,
"data": {
"token": "xtk_abc123",
"status": "completed",
"age_result": { "method": "age_estimation", "passed": true },
"country_code": "US",
"created_at": "2026-03-30T10:30:00Z",
"expires_at": "2026-03-30T10:40:00Z"
},
"meta": {
"request_id": "req_xyz789",
"timestamp": "2026-03-30T10:30:05Z"
}
}
data.status is the authoritative outcome (completed = passed, failed, canceled). Sub-objects such as age_result, liveness_result, and ocr_result carry method-level detail — see the API Reference for the full schema.
Testing
For development, use your test key and a localhost callback:
sk_test_…secret key (sandbox mode)http://localhost:*callback URLs (no HTTPS required)
Next Steps
- Core Concepts — Xident ID, verification paths, and the two-token model
- Integration Guide — advanced options and mobile SDKs
- Dashboard Guide — monitor usage and analytics
- API Reference — full generated API documentation