Developer Portal

Fraud Detection API — Integration Guide

REST API with JWT authentication. 38-feature ML scoring, SHAP explanations, label feedback loop, and real-time drift monitoring — all in one endpoint.

All code examples on this page are verified against the live Remorant API. The API uses JWT Bearer tokens (not opaque fsk_live_ keys) and requires a 38-element features array — not a transaction-field object. See the Feature Schema section below.
Authentication

How authentication works

Every API call (except GET /health and GET /info) requires a JWT Bearer token. Obtain one by logging in with your Remorant email and password. Tokens expire after 24 hours.

1
Login
POST https://remorant.com/api/v1/auth/login with email + password
2
Extract token
Copy the token string from the response JSON
3
Attach to requests
Add Authorization: Bearer <token> to every request

Token roles & permissions

RoleCan predictCan labelCan read statsAdmin
platform_admin✓ Full
tenant_admin✓ Tenant
analyst
integration
readonly

Machine-to-machine integrations should use a user account with the integration role. Create one in the admin panel under Team Management.

Quickstart

Integration in 3 steps

Login → build feature vector → score. All examples call the real API endpoint.

Step 1 — Obtain a JWT tokenbash
curl -X POST https://remorant.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@yourcompany.com",
    "password": "your-password"
  }'
Step 1 responsejson
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": { "id": "usr_...", "email": "you@yourcompany.com", "role": "integration" },
  "tenant": { "id": "ten_...", "name": "Your Company", "plan": "growth" }
}
Step 2 — Score a transactionbash
# TOKEN from Step 1 response
curl -X POST https://remorant.com/api/v1/predict \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "features": [0, -0.31, 1.19, 1.03, 0.99, 0.84, -0.11, 0.42, 0.16, 0.07, 0.06, -0.34, 0.34, -0.2, 0.21, 0.09, 0.42, 0.15, 0.01, -0.13, -0.04, -0.08, -0.15, 0.02, -0.05, -0.21, -0.07, 0.11, -0.05, 47.5, 14, 0.4, 3.87, 0, 0, 0.2, 0, 3.87],
    "transaction_id": "txn_abc123"
  }'
Step 2 responsejson
{
  "request_id": "req_7f3a9e2d",
  "transaction_id": "txn_abc123",
  "fraud_probability": 0.0842,
  "is_fraud": false,
  "risk_level": "low",
  "recommended_action": "ALLOW",
  "threshold": 0.5802,
  "threshold_source": "tenant_optimized",
  "model_version": "1.0.0",
  "latency_ms": 38,
  "explanation": {
    "top_features": [
      { "feature": "V14", "shap_value": -0.18, "value": 0.21 },
      { "feature": "Amount", "shap_value":  0.05, "value": 47.5 },
      { "feature": "V17", "shap_value": -0.03, "value": 0.15 }
    ]
  }
}
Step 3 — Submit a fraud label (feedback loop)bash
# After a chargeback or manual review, send the confirmed outcome back.
# This is used for model drift monitoring and future retraining.
curl -X POST https://remorant.com/api/v1/label \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "request_id": "req_7f3a9e2d",
    "actual_label": 0
  }'
# actual_label: 1 = confirmed fraud, 0 = confirmed legitimate
Feature Schema

The 38-element feature vector

The features array must contain exactly 38 floats in the canonical order below. The authoritative column list is always available at GET https://remorant.com/info → feature_columns.

V1–V28 are anonymized risk signal components that must be supplied by your card-network data feed or preprocessing pipeline. The exact field mapping depends on your network provider — contact our integrations team for guidance.
Endpoints

API reference

POST/api/v1/auth/loginpublicExchange email + password for a JWT token (24h expiry)
POST/api/v1/predictJWT requiredScore a transaction. Body: { features: number[38], transaction_id?: string }
POST/api/v1/batchJWT requiredBatch scoring, up to 1 000 transactions per call
POST/api/v1/labelJWT requiredSubmit confirmed outcome: { request_id, actual_label: 0|1 }
GET/api/v1/statsJWT requiredPrediction statistics for your tenant
GET/infopublicModel metadata: version, feature_columns, threshold, metrics
GET/healthpublicLiveness probe — returns { status: "ok" }
GET/api/v1/healthpublicDetailed service health: API, database, cache, model registry status and uptime
GET/readypublicReadiness probe — model and database must be operational
GET/metricspublicInfrastructure metrics endpoint (available on Enterprise plans)
Rate Limits

Rate limits by plan

Rate limits are enforced per API credential using sliding-window counters. Limits reset on a rolling basis — exceeding your limit returns HTTP 429 with a Retry-After header indicating when you may resume.

PlanPer minutePer hourPer day
Trial202001,000
Starter1002,00010,000
Growth50010,00050,000
Enterprise2,00040,000500,000

Rate limit headers returned on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. On 429, also Retry-After (seconds).

Sandbox

Sandbox testing

Add "is_sandbox": true to any POST /api/v1/predict request during development and QA. Sandbox calls return the exact same response format as production but are never persisted, never billed, and never affect your analytics or drift monitoring.

Sandbox predict requestbash
curl -X POST https://remorant.com/api/v1/predict \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "features": [0, -0.31, 1.19, 1.03, 0.99, 0.84, -0.11, 0.42, 0.16, 0.07, 0.06, -0.34, 0.34, -0.2, 0.21, 0.09, 0.42, 0.15, 0.01, -0.13, -0.04, -0.08, -0.15, 0.02, -0.05, -0.21, -0.07, 0.11, -0.05, 47.5, 14, 0.4, 3.87, 0, 0, 0.2, 0, 3.87],
    "transaction_id": "test_txn_dev_001",
    "is_sandbox": true
  }'

# Sandbox predictions:
#  Same response format as production
#  NOT saved to the database
#  NOT counted toward your billing quota
#  NOT included in analytics or drift monitoring
# Omit is_sandbox (or set false) in production
Error Handling

Error codes

All errors return a structured JSON body. Handle errors by error code, not by HTTP status alone — multiple error codes can share the same HTTP status.

Error response shapejson
{ "error": "MISSING_FEATURES", "message": "features must contain exactly 38 numeric values" }
StatusError codeCauseRecommended action
400MISSING_FEATURESfeatures array is absent or does not contain exactly 38 elementsValidate array length before sending
400INVALID_FORMATfeatures contains non-numeric values or request body is invalid JSONEnsure every element is a finite float
400INVALID_INPUTRequest body fails schema validationCheck field names and types against the API reference
401AUTHENTICATION_FAILEDJWT token is missing, malformed, or has expired (24 h TTL)Re-authenticate via POST /api/v1/auth/login and retry
401INVALID_CREDENTIALSEmail or password is incorrectVerify credentials — do not retry in a tight loop
403ACCOUNT_DISABLEDAccount has been suspendedContact support@remorant.com
403INSUFFICIENT_PERMISSIONSToken role cannot perform this actionUse a token with the integration or analyst role
429RATE_LIMIT_EXCEEDEDSliding-window rate limit hit for your planRead Retry-After header and wait before retrying
503MODEL_NOT_AVAILABLEPrediction model is temporarily unavailableCheck GET /health — contact support if the outage persists
500PREDICTION_ERRORUnexpected internal error during scoringRetry with exponential backoff; open a support ticket if it persists
Webhooks

Webhook integration

Remorant signs every webhook delivery with an HMAC-SHA256 signature in the X-Remorant-Signature header. Always verify the signature before processing the payload.

Available events
transaction.fraud_detectedFraud probability exceeds 0.80
transaction.high_riskRisk level is high or critical
transaction.blockedRecommended action is BLOCK
drift.detectedFeature distribution drift detected
rate_limit.exceededTenant rate limit was hit

Subscribe to events in Settings → Webhooks.

Signature verification — Python (Flask)python
import hmac, hashlib, os
from flask import Flask, request

def verify_signature(payload_bytes: bytes, header_sig: str, secret: str) -> bool:
    """Return True if the webhook signature is valid."""
    expected = hmac.new(
        secret.encode(),
        payload_bytes,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, header_sig)

app = Flask(__name__)

@app.route("/remorant-webhook", methods=["POST"])
def handle_webhook():
    ok = verify_signature(
        request.get_data(),                              # raw bytes — before JSON parsing
        request.headers.get("X-Remorant-Signature", ""),
        os.environ["REMORANT_WEBHOOK_SECRET"],
    )
    if not ok:
        return {"error": "Invalid signature"}, 401

    event = request.json
    if event["event_type"] == "transaction.fraud_detected":
        handle_fraud_alert(event["data"])
    elif event["event_type"] == "transaction.blocked":
        handle_blocked(event["data"])
    elif event["event_type"] == "drift.detected":
        notify_ops_team(event["data"])

    return {"received": True}, 200
Signature verification — Node.js (Express)javascript
import crypto from 'crypto';
import express from 'express';

function verifySignature(rawBody, headerSig, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  // timingSafeEqual prevents timing-based attacks
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(headerSig));
}

const app = express();

// Use express.raw() to preserve raw bytes for signature verification
app.post('/remorant-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-remorant-signature'] ?? '';
  if (!verifySignature(req.body, sig, process.env.REMORANT_WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const event = JSON.parse(req.body);
  switch (event.event_type) {
    case 'transaction.fraud_detected': handleFraudAlert(event.data); break;
    case 'transaction.blocked':        handleBlocked(event.data);    break;
    case 'drift.detected':             notifyOps(event.data);        break;
  }
  res.json({ received: true });
});
Always read the raw request bytes before JSON parsing — parsers may reorder keys and invalidate the signature. Use express.raw() in Node.js or request.get_data() in Flask.
Resources

Developer resources

API Playground

Test real fraud detection scenarios interactively with live results and SHAP explanations.

Webhooks

Receive real-time fraud events over HTTPS with HMAC-SHA256 signature verification.

Analytics Dashboard

Live fraud rates, SHAP feature trends, drift alerts, and model performance.

Full API Reference

Detailed docs are available in the repo at docs/. An interactive OpenAPI spec is on the roadmap.

SDKs (in development)

Python SDK at sdks/python/ and JS SDK at sdks/javascript/ are available in the repo. A high-level check_transaction() interface is on the roadmap pending a server-side feature builder endpoint.

Integration Support

Need help mapping your card-network fields to the feature vector? Contact our integrations team.

Ready to integrate?

Create your account, get a JWT, and make your first prediction in under 5 minutes.