API Reference
Complete endpoint documentation for AccessGate fraud detection and continuous authentication.
Production URL: https://ag.runloci.com
Staging URL: https://ag-staging.runloci.com
Authentication
All API requests require authentication via headers:
x-org-id: your_organization_id
x-api-key: your_api_key
Keep your API key secure. Never expose it in client-side code or public repositories.
Core Endpoints
Risk Check
/v1/checkVerify User
Analyzes behavioral signals and returns a fraud decision. This is the primary endpoint for all verification requests.
Request Headers:
x-org-id(required) — Your organization IDx-api-key(required) — Your API keyContent-Type: application/json
Request Body:
{
"email": "user@example.com",
"ip": "102.88.34.45",
"phone": "+2348012345678",
"device": {
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
"fingerprint": {
"screen": { "width": 1920, "height": 1080, "colorDepth": 24 },
"timezone": "Africa/Lagos",
"language": "en-US",
"canvas": "a1b2c3d4",
"hardware": { "cores": 8, "memory": 16 }
}
},
"behavior": {
"mouseMovements": 245,
"clickPattern": "human",
"keystrokeTimings": [120, 150, 200],
"scrollBehavior": "natural",
"timeOnPage": 45000,
"formFillSpeed": 2.5
},
"behavioral": {
"mouse": [
{ "type": "move", "x": 100, "y": 200, "timestamp": 1700000001000 },
{ "type": "click", "x": 102, "y": 202, "timestamp": 1700000001500 }
],
"keystrokes": [
{ "key": "Shift", "type": "keydown", "timestamp": 1700000002000 },
{ "key": "A", "type": "keydown", "timestamp": 1700000002100 }
]
},
"context": {
"action": "login",
"user_id": "user_123",
"session_id": "sess_abc123",
"timestamp": "2026-01-22T10:00:00Z"
},
"expand": true
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | One of email/ip/phone required | User's email address |
ip |
string | One of email/ip/phone required | Client IP address (IPv4 or IPv6) |
phone |
string | One of email/ip/phone required | Phone number (E.164 format recommended) |
device |
object | Recommended | Device fingerprint data from SDK |
behavior |
object | Recommended | Aggregated behavioral signals |
behavioral |
object | Recommended | Raw behavioral events (mouse, keystrokes) |
context.action |
string | Required | Action type: signup, login, payment, password_reset, profile_update, default |
context.user_id |
string | Recommended | Your unique user identifier |
context.session_id |
string | Optional | Your session identifier |
expand |
boolean | Optional | Return expanded response details |
Response: 200 OK
{
"request_id": "req_550e8400-e29b-41d4-a716-446655440000",
"decision": {
"outcome": "allow",
"score": 23,
"confidence": 0.88,
"reasons": []
},
"metadata": {
"processing_time_ms": 45,
"baseline_sessions": 15
}
}
Decision Outcomes:
| Outcome | Score Range | Description |
|---|---|---|
allow |
0-39 | Low risk, proceed normally |
review |
40-79 | Medium risk, request additional verification |
block |
80-100 | High risk, deny access |
Evaluation Mode (Debug)
:::api POST /v1/eval/check
Glass Box Evaluation
Returns detailed signal breakdown for debugging and POCs. Same payload as /v1/check.
:::
Request: Same as /v1/check
Response: 200 OK
{
"eval_mode": true,
"decision": "ALLOW",
"risk_score": 23,
"signals": {
"biometrics": "APPROVED",
"stability_score": 0.85,
"entropy_score": 0.72,
"reasoning": []
},
"context": {
"ip_reputation": "Clean",
"network": "MTN Nigeria",
"baseline_sessions": 15
},
"original_response": { }
}
The eval endpoint reveals internal signal details. Use only in development and staging environments.
Reason Codes
When a request is flagged, the reasons array contains specific indicators:
IP & Network Signals
| Reason | Description |
|---|---|
Tor exit node detected |
Traffic from Tor network |
VPN detected |
VPN service detected |
Proxy detected |
Proxy server detected |
Datacenter IP |
IP belongs to a datacenter/hosting provider |
Known fraud IP |
IP associated with previous fraud |
High-risk country: {name} |
Traffic from high-risk jurisdiction |
Email Signals
| Reason | Description |
|---|---|
Disposable email domain |
Temporary/disposable email service |
Suspicious email pattern |
Pattern matching fraud indicators |
Disposable keyword in email |
Email contains disposable service keywords |
Random character pattern detected |
Randomly generated email address |
Long numeric sequence in email |
Unusual numeric pattern |
Suspicious TLD: .{tld} |
High-risk top-level domain |
Phone Signals
| Reason | Description |
|---|---|
Invalid phone number format |
Phone number failed validation |
Phone number validation failed (HLR) |
HLR lookup returned invalid |
VoIP number detected |
Virtual phone number |
Recently ported number (SIM swap risk) |
Number recently changed carriers |
SIM swap detected ({risk} risk, {days} days ago) |
Recent SIM swap activity |
Behavioral Signals
| Reason | Description |
|---|---|
Bot detected ({confidence}% confidence) |
Automated behavior patterns |
Non-human mouse patterns |
Mouse movement indicates automation |
Automated keystrokes |
Keystroke timing indicates bot |
Unnatural session behavior |
Session patterns don't match human behavior |
Emulator detected (Perfect Stability) |
Device emulator identified |
Biometric deviation (score: {score}) |
Behavior differs significantly from baseline |
Velocity & Travel
| Reason | Description |
|---|---|
Velocity exceeded ({count} requests) |
Too many requests in time window |
Impossible travel detected |
Location change too fast to be legitimate |
Physically impossible: {speed} km/h |
Travel speed exceeds physical possibility |
Session & Device
| Reason | Description |
|---|---|
New device detected |
Device not seen before for this user |
New country: {country} |
Login from unusual country |
Unusual time: {hour}:00 |
Activity at unusual hour for user |
Account less than 1 day old |
Very new account |
Session anomalies: {patterns} |
Suspicious session patterns |
Behavioral Deviation (Continuous Auth)
| Reason | Description |
|---|---|
High mouse deviation: {pct}% from baseline |
Mouse patterns differ from baseline |
High keystroke deviation: {pct}% from baseline |
Typing patterns differ from baseline |
High session deviation: {pct}% from baseline |
Session behavior differs from baseline |
Rapid device addition detected |
Multiple new devices added quickly |
Behavioral divergence across devices |
Inconsistent behavior across devices |
Low behavioral consistency: {pct}% |
Overall behavior inconsistency |
Fraud Ring Indicators
| Reason | Description |
|---|---|
High fraud ring score: {score}/100 |
Strong fraud ring indicators |
Moderate fraud ring indicators detected |
Some fraud ring patterns |
Device added within 24h of another (fraud ring pattern) |
Rapid device addition pattern |
Suspicious device sharing pattern |
Devices shared across accounts |
Whitelist Management
Add to Whitelist
/v1/whitelistAdd Whitelist Entry
Add a user, IP, email, phone, or device to the whitelist.
Request Body:
{
"entity_type": "ip",
"identifier": "203.0.113.50",
"reason": "Corporate VPN Exit Node",
"duration_hours": 168,
"verified_by": "admin@company.com",
"verification_method": "manual_review"
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
entity_type |
string | Yes | One of: user, email, ip, phone, device_fingerprint |
identifier |
string | Yes | The value to whitelist (max 255 chars) |
reason |
string | Yes | Audit trail reason (10-500 chars) |
duration_hours |
integer | No | How long to whitelist (1-720 hours, default: 168/7 days) |
verified_by |
string | Yes | Who verified this entry |
verification_method |
string | Yes | One of: phone_callback, document_review, manual_review, challenge_flow |
Response: 201 Created
{
"id": "wl_abc123",
"entity_type": "ip",
"identifier": "203.0.113.50",
"created_at": "2026-01-25T10:30:00Z",
"expires_at": "2026-02-01T10:30:00Z"
}
Remove from Whitelist
/v1/whitelist/{id}Remove Whitelist Entry
Remove an entity from the whitelist.
Parameters:
id(path, required) — Whitelist entry ID
Response: 204 No Content
Feedback System
Report Feedback
/v1/feedbackSubmit Feedback
Report whether a decision was correct or incorrect. Used to improve accuracy.
Request Body:
{
"request_id": "req_550e8400-e29b-41d4-a716-446655440000",
"feedback_type": "false_positive",
"actual_outcome": "allow",
"reported_by": "support_agent_01",
"notes": "User was traveling, confirmed via phone call."
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
request_id |
string | Yes | Original request ID |
feedback_type |
string | Yes | One of: false_positive, false_negative, correct |
actual_outcome |
string | Yes | What should have happened: allow, review, block |
reported_by |
string | Yes | Who reported this feedback |
notes |
string | No | Additional context (max 1000 chars) |
Response: 201 Created
{
"feedback_id": "fb_ghi789",
"received_at": "2026-01-25T10:30:00Z"
}
Challenge Flow (Step-Up Auth)
Issue Challenge
/v1/challenge/issueIssue Challenge
Send an OTP or verification challenge to the user.
Request Body:
{
"challenge_type": "email_otp",
"email": "user@example.com",
"request_id": "req_8888",
"metadata": {
"source": "login_screen"
}
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
challenge_type |
string | Yes | One of: email_otp, phone_otp |
email |
string | Required if email_otp | User's email address |
phone |
string | Required if phone_otp | User's phone number |
request_id |
string | Yes | Original request ID that triggered challenge |
Response: 201 Created
{
"challenge_id": "ch_7777",
"type": "email_otp",
"expires_at": "2026-01-25T10:35:00Z"
}
Verify Challenge
/v1/challenge/verifyVerify Challenge Response
Validate the user's OTP code.
Request Body:
{
"challenge_id": "ch_7777",
"code": "123456"
}
Response: 200 OK
{
"verified": true,
"decision": {
"outcome": "allow",
"reasons": ["Step-up authentication completed"]
}
}
Error Response: 400 Bad Request
{
"verified": false,
"error": "invalid_code"
}
Configuration
Update Risk Weights
/v1/weightsUpdate Risk Configuration
Configure scoring weights and thresholds for your organization.
Request Body:
{
"config_name": "Strict Login Policy",
"is_active": true,
"thresholds": {
"block": 80,
"review": 40,
"challenge": 60
},
"ip": {
"tor": 50,
"vpn": 20
},
"velocity": {
"abuse": 100
},
"features": {
"behavioral_entropy": true,
"continuous_auth": true
}
}
Response: 200 OK
Get Current Weights
/v1/weightsGet Risk Configuration
Retrieve current scoring configuration.
Response: 200 OK
{
"config_name": "Strict Login Policy",
"is_active": true,
"thresholds": {
"block": 80,
"review": 40,
"challenge": 60
},
"features": {
"behavioral_entropy": true,
"continuous_auth": true
}
}
GDPR & Sessions
Get Active Sessions
/sessions/{entity_id}Get User Sessions
Retrieve all active sessions for a user.
Response: 200 OK
Export User Data
/gdpr/export/{entity_id}Export User Data
Export all data stored for a user (GDPR Article 15).
Response: 200 OK
Delete User Data
/gdpr/delete/{entity_id}Delete User Data
Permanently delete all data for a user (GDPR Article 17).
Response: 200 OK
{
"entity_id": "user_123",
"deleted_at": "2026-01-25T10:30:00Z"
}
This action is irreversible. The user's behavioral baseline will need to be re-established.
Webhooks
Configure Webhook
/v1/webhooks/configConfigure Webhook
Set up webhook notifications for risk events.
Request Body:
{
"url": "https://your-backend.com/webhooks/accessgate",
"secret": "whsec_...",
"events": ["risk.high", "risk.low"],
"enabled": true
}
Response: 200 OK
System
Health Check
/healthHealth Check
Check API availability.
Response: 200 OK
{
"status": "healthy",
"timestamp": "2026-01-25T10:30:00Z"
}
Error Handling
Error Response Format
{
"error": "error_code",
"message": "Human-readable description",
"details": {}
}
HTTP Status Codes
| Status | Description |
|---|---|
200 |
Success |
201 |
Created |
204 |
No Content (successful deletion) |
400 |
Bad Request — Invalid parameters |
401 |
Unauthorized — Invalid or missing API key |
403 |
Forbidden — Insufficient permissions |
404 |
Not Found — Resource doesn't exist |
429 |
Too Many Requests — Rate limit exceeded |
500 |
Internal Server Error — Contact support |
Code Examples
Python
import requests
response = requests.post(
'https://ag.runloci.com/v1/check',
headers={
'x-org-id': 'your_org_id',
'x-api-key': 'your_api_key',
'Content-Type': 'application/json'
},
json={
'email': 'user@example.com',
'ip': '102.88.34.45',
'context': {
'action': 'login',
'user_id': 'user_123'
}
}
)
result = response.json()
print(f"Decision: {result['decision']['outcome']}")
Node.js
const response = await fetch('https://ag.runloci.com/v1/check', {
method: 'POST',
headers: {
'x-org-id': process.env.ACCESSGATE_ORG_ID,
'x-api-key': process.env.ACCESSGATE_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'user@example.com',
ip: '102.88.34.45',
context: {
action: 'login',
user_id: 'user_123'
}
})
});
const result = await response.json();
console.log(`Decision: ${result.decision.outcome}`);
cURL
curl -X POST "https://ag.runloci.com/v1/check" \
-H "x-org-id: your_org_id" \
-H "x-api-key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"ip": "102.88.34.45",
"context": {
"action": "login",
"user_id": "user_123"
}
}'
All code examples are ready to use. Replace credentials with your own.
Support
- Email: support@runloci.com
- Status: https://status.runloci.com
- Documentation: https://docs.runloci.com/accessgate