CycloudForte
ArcheaOne

Documentation

ArcheaOne Documentation

Everything you need to integrate real-time fraud decisions into your payment flow. Integrate in under 30 minutes.

Overview

ArcheaOne adds real-time fraud detection to your payment flow. Before a transaction is processed, you send it to ArcheaOne and get a fraud decision back in milliseconds: BLOCK, REVIEW or ALLOW.

You keep full ownership of your transactions, logins and account flows. At the moments where you want a risk decision, you send an event. ArcheaOne scores it with a rules engine and a machine-learning anomaly model working together, and returns a decision with reasons. The model can escalate an ALLOW to REVIEW, but never relaxes a rule decision.

Blocked activity also appears as an alert in your dashboard, and signed webhooks go out to your systems.

Where ArcheaOne sits in your transaction flow

  1. 1

    Your customer

    Starts a transfer or payment

  2. 2

    Your backend

    Sends the transaction to ArcheaOne before moving money

  3. 3

    ArcheaOne API

    Returns BLOCK, REVIEW or ALLOW with a risk score and reason codes

  4. 4

    Your backend

    Acts on the decision and stays in control

BLOCK

Decline the transaction

REVIEW

Hold it: step-up auth or manual review

ALLOW

Commit the debit and process normally

On BLOCK, ArcheaOne also shows a fraud alert in your dashboard and sends a signed webhook, Slack message or email for HIGH and CRITICAL risk.

Common integration patterns

  • Synchronous gate on money movement. Call assess/transaction before you commit the debit. On BLOCK, stop. On REVIEW, hold the transaction for manual release. On ALLOW, proceed.
  • Asynchronous enrichment on login. Call assess/access after a login attempt without blocking on it, and let the webhook drive any follow-up, such as step-up MFA or a forced password reset.
  • Passive observability on everything else. Send events through assess to build up a behavioural baseline, and start gating later.
We recommend starting with passive observability for the first week so the model can learn your traffic. Turn on synchronous gating once the false-positive rate is where you want it.

Getting started

  1. Get an account. and we will set up your organisation.
  2. Create an API key. In the dashboard, go to API Keys → Create. Copy the full key right away, because only its prefix can be shown later.
  3. Pick a mode.
    • Test mode (keys start with rsk_test_): sandbox traffic. Test calls don't count toward your monthly usage and have no monthly cap.
    • Live mode (keys start with rsk_live_): production traffic. Every successful live assessment counts toward your plan's monthly usage (see Usage and billing).
  4. Send your first transaction (see Evaluate a transaction).
  5. Check the dashboard. Open Fraud Alerts. A blocked event appears at the top of the list within a couple of seconds.
  6. Optional: set up alert delivery by email, Slack or your own webhook (see Alerts and webhooks).

Base URL

Base URL
https://api.archeaone.com/api/v1

Authentication

Send your API key with every request, in either header:

Header
x-api-key: rsk_test_...
Header
Authorization: Bearer rsk_test_...
  • The key identifies your organisation. Don't send an organizationId in the request body: unknown fields are rejected with 400. You may send an x-organization-id header, but it must match the key's organisation, or the call fails with 401.
  • Only call the API from your backend. Never ship an API key in a mobile app or browser code.
  • A missing, invalid, expired or revoked key returns 401.

Evaluate a transaction

POST/risk-assessment/assess/transaction

Use this for money movement: transfers, payments, payouts, debits, credits and refunds.

Request body

All fields are required unless marked optional. Fields not listed here are rejected with 400. Put any extra context in metadata.

FieldTypeDescription
eventIdstringYour ID for this event.
eventTypestring"TRANSACTION"
timestampISO 8601 stringWhen the event happened.
transactionIdstringYour transaction ID.
accountIdstringThe account being debited.
amountnumberTransaction amount.
currencystringISO currency code, e.g. NGN.
statusenumPENDING, SUCCESS, FAILED or CANCELLED
transactionTypeenumDEBIT, CREDIT, TRANSFER, PAYMENT or REFUND
actorobjectWho is acting (see below).
deviceobjectThe device used (see below).
correlationIdstring, optionalYour own ID for linking related events.
metadataobject, optionalAny extra key/value context.
simSwapobject, optionalThe result of your own SIM swap check (see below).

actor

FieldTypeDescription
userIdstringYour ID for the end user.
userTypestringe.g. CUSTOMER.
emailstring, optionalMust be a valid email if sent.
phonestring, optional
rolestring, optional
permissionsstring[], optional

device

FieldTypeDescription
deviceIdstringStable device identifier.
sessionIdstringCurrent session ID.
deviceNamestringe.g. iPhone 14.
deviceTypestringe.g. MOBILE, WEB, DESKTOP.
deviceTokenstringDevice or push token.
osstringe.g. iOS 17.
locationobjectlatitude (−90 to 90), longitude (−180 to 180), country, city (all required).
lastSeenISO 8601 stringWhen the device was last seen.

simSwap (optional)

Payments and logins made soon after a customer's SIM card was swapped are a common sign of account takeover. If you already check SIM swap status, for example through the NIBSS SIM swap service, send the result here and ArcheaOne will take it into account. A recent swap adds the SIM_SWAP_RECENT reason code. It raises the risk but does not block on its own, so a customer who genuinely replaced a lost SIM is sent for review rather than refused. Works on transaction and login events.

FieldTypeDescription
lastSwappedAtISO 8601 string, optionalWhen the customer's SIM was last swapped.
swappedRecentlyboolean, optionalSend true if your check only says the SIM was swapped recently, without a date.
sourcestring, optionalWhere the result came from, e.g. NIBSS. For your own records.
simSwap
"simSwap": {
  "lastSwappedAt": "2026-10-13T08:30:00Z",
  "source": "NIBSS"
}

Examples

cURL
curl -X POST "https://api.archeaone.com/api/v1/risk-assessment/assess/transaction" \
  -H "Content-Type: application/json" \
  -H "x-api-key: rsk_test_..." \
  -d '{
    "eventId": "evt_001",
    "eventType": "TRANSACTION",
    "timestamp": "2026-06-03T15:00:00.000Z",
    "transactionId": "txn_456",
    "accountId": "acc_demo_001",
    "amount": 50000,
    "currency": "NGN",
    "status": "PENDING",
    "transactionType": "TRANSFER",
    "actor": {
      "userId": "user_123",
      "userType": "CUSTOMER",
      "email": "customer@example.com"
    },
    "device": {
      "deviceId": "dev_abc123",
      "sessionId": "sess_xyz789",
      "deviceName": "iPhone 14",
      "deviceType": "MOBILE",
      "deviceToken": "token_abc",
      "os": "iOS 17",
      "location": {
        "latitude": 6.5244, "longitude": 3.3792,
        "country": "NG", "city": "Lagos"
      },
      "lastSeen": "2026-06-03T15:00:00.000Z"
    }
  }'

Response

The default response is kept small: it contains only what your application needs to act on a decision.

200 OK
{
  "status": 200,
  "message": "Transaction risk assessment completed",
  "data": {
    "assessmentId": "RISK-...",
    "eventId": "evt_001",
    "userId": "user_123",
    "decision": "BLOCK",
    "riskScore": 87,
    "riskLevel": "CRITICAL",
    "reasons": [
      "Transaction from new country: RU"
    ],
    "reasonCodes": [
      "GEOGRAPHIC_ANOMALY", "UNUSUAL_AMOUNT"
    ],
    "assessedAt": "2026-06-03T15:00:00.000Z",
    "assessor": "SYSTEM",
    "metadata": {
      "processingTimeMs": 124
    }
  }
}
FieldDescription
assessmentIdArcheaOne's ID for this assessment.
eventId, userIdEchoed from your request.
decisionBLOCK, REVIEW or ALLOW. This is what you act on.
riskScore0–100 severity signal.
riskLevelLOW, MEDIUM, HIGH or CRITICAL.
reasonsHuman-readable explanations.
reasonCodesMachine-readable codes for the rules that fired.
assessedAtWhen the assessment was made.
metadata.processingTimeMsServer processing time, useful for monitoring latency.
metadata.replayedPresent and true only when this is a repeat of an eventId you already sent.

Don't branch on message; its wording differs between endpoints.

More operational detail (optional)

Append ?include=metadata to get a few more operational fields. It doesn't cost anything extra; it's the same single call.

metadata
"metadata": {
  "processingTimeMs": 124,
  "evaluatedRules": 2,
  "eventType": "TRANSACTION",
  "mode": "test",
  "profileAge": 42,
  "location": { "latitude": 6.5244, "longitude": 3.3792, "country": "NG", "city": "Lagos" },
  "deviceId": "dev_abc123"
}

evaluatedRules is the number of rules that fired. profileAge is the age of the end user's behaviour profile in days. location and deviceId echo what you sent.

Other event types

Login and access events

POST/risk-assessment/assess/access

Takes the same eventId, eventType ("ACCESS"), timestamp, actor, device and optional correlationId / metadata fields as a transaction, plus:

FieldTypeDescription
actionenumLOGIN, LOGOUT, PASSWORD_RESET, FAILED_LOGIN or TWO_FA_CHALLENGE
successbooleanWhether the attempt succeeded.
accessMethodenumPASSWORD, FINGER_PRINT, FACE_ID or 2FA
noOfTriesnumber (≥ 0)Attempts in this session.
ipAddressstringThe end user's IP address.
reasonstring, optionale.g. "Invalid password".

Any other event

POST/risk-assessment/assess

Takes only the common fields (eventId, eventType, timestamp, actor, device, and optional correlationId / metadata). Put any event-specific details in metadata. Behavioural rules run for eventType values BEHAVIOR and ACCOUNT. Other event types are stored to build the user's history, but no built-in rules run on them, so they return ALLOW.

All three endpoints return the same response shape.

Decisions and reason codes

DecisionMeaningWhat to do
BLOCKHigh risk.Stop it. Don't process the transaction, and return an error to the user.
REVIEWSuspected fraud.Hold the transaction, step up authentication, or send it to manual review.
ALLOWNo elevated risk.Process the transaction normally.
Always branch on decision, not on riskScore. The decision is authoritative. A high-severity rule can return BLOCK for a transaction whose score is in the mid range, so a score cut-off of your own would let real fraud through. Use riskScore and riskLevel only as severity signals for dashboards and thresholds.

Reason codes

CodeApplies toWhat it flags
TRANSACTION_VELOCITYTransactionsMany transactions in a short time
UNUSUAL_AMOUNTTransactionsAmount far outside the user's normal range
GEOGRAPHIC_ANOMALYTransactionsTransaction from a new or unusual country
RAPID_LOCATION_CHANGETransactionsLocation change too fast to be physically possible
NIGHTTIME_TRANSACTIONTransactionsActivity at unusual hours
SMALL_TRANSFER_VELOCITYTransactionsBurst of small transfers (card or account testing)
NEW_ACCOUNT_PROBETransactionsNew account probing with early transactions
STRUCTURING_DETECTIONTransactionsAmounts split to stay under thresholds
SIM_SWAP_RECENTTransactions and accessActivity soon after the customer's SIM was swapped (needs simSwap)
FAILED_LOGIN_ATTEMPTSAccessRepeated failed logins
NEW_DEVICE_LOGINAccessLogin from a device not seen before
SUSPICIOUS_IPAccessLogin from a suspicious IP address
HISTORICAL_RISK_PATTERNBehaviour / accountUser has a history of risky activity
NEW_ACCOUNTBehaviour / accountVery new account
CUSTOM_RULEAnyOne of your own dashboard rules matched (named in reasons)

New codes may be added over time, so treat unknown codes as informational rather than as errors.

Errors and rate limits

Every error returns the same JSON shape. message is a string, or for validation errors, an array of strings.

400 Bad Request
{
  "status": 400,
  "message": ["actor.userId should not be empty", "property organizationId should not exist"],
  "timestamp": "2026-06-03T15:00:00.000Z",
  "path": "/api/v1/risk-assessment/assess/transaction"
}
StatusWhenWhat to do
400A required field is missing, an enum value is invalid, or the body contains a field that isn't in the schema.Fix the payload. message lists each problem.
401The API key is missing, invalid, expired or revoked, or x-organization-id doesn't match the key.Check the key and header.
403Your plan's monthly live-call limit is reached, or your organisation is suspended.Upgrade your plan, or contact support.
409Another request with the same eventId is still being scored (for example, a retry sent while the original was in flight).Retry after a moment: you'll get the original result.
429Too many requests in a short window.Back off and retry.
500ArcheaOne couldn't complete the assessment.Retry with backoff, and apply your fallback policy.

Rate limits

The assessment endpoints (assess, assess/transaction, assess/access) accept, per client IP address:

WindowRequests
1 second25
10 seconds200
1 minute1,000

Going over any window returns 429. Back off and retry. If you need higher limits, contact support.

Timeouts and fallback

Set a client timeout on every call (the Python example uses 5 seconds) and decide in advance what happens if ArcheaOne is unreachable or returns 5xx: either let the transaction through (fail open) or hold it for review (fail closed). Most teams fail open for low-value transactions and fail closed for high-value ones.

Usage and billing

One API call is one successful live-mode assessment: a 200 response from assess/transaction, assess/access or assess made with an rsk_live_ key.

SituationCounts toward usage?
Assessment returns BLOCK, REVIEW or ALLOWYes. All decisions count the same.
The transaction later fails, is declined or cancelled in your systemYes. The assessment already happened. The status you send makes no difference.
Request rejected by ArcheaOne: 400, 401, 403, 409, 429No. Nothing is scored.
Assessment fails with 500No.
You retry with the same eventId within 24 hoursNo. You get the original result back, with metadata.replayed: true. It isn't scored or counted again, and no new alerts are sent.
You send the same eventId after 24 hours, or a new eventId for the same transactionYes. It's treated as a new event. Use one eventId per event and reuse it only for retries.
Internal transactions (own-account transfers, reversals, fee postings)Yes, if you send them. ArcheaOne doesn't know a transaction is internal, so send only the events you want scored.
?include=metadataNo extra charge. It's the same single call.
Test-mode (rsk_test_) callsNo.
Webhook, Slack and email alert deliveriesNo.
Dashboard use, managing API keys, webhooks and rulesNo.

Monthly limits by plan

PlanLive assessments per month
Free Trial (14 days)1,000, the same as Starter, so you can try it before paying
Starter1,000
Growth10,000
EnterpriseUnlimited

Usage resets at 00:00 UTC on the 1st of each month. When you reach your limit, further live calls return 403 until the next month or until you upgrade. Retries of events already scored within 24 hours still return their result. There are no automatic overage charges. Your dashboard shows this month's usage and when it resets.

Alerts and webhooks

When an assessment returns BLOCK, ArcheaOne:

  • creates a fraud alert in your dashboard;
  • sends a signed webhook to each active endpoint subscribed to HIGH_RISK_DETECTED;
  • emails your organisation's contact address when the risk level is HIGH or CRITICAL (you can turn this off in Settings).

Alerts from test-mode traffic appear in your dashboard and go only to webhooks you created with a test key. They never send email, so you can test safely without paging your production channels.

Repeated blocks for the same user and event type within a short cool-down window produce one alert, not many. Pattern detection can also raise alerts: critical patterns are sent as HIGH_RISK_DETECTED, and others as ALERT_CREATED.

Set up webhooks in the dashboard under Webhooks. Choose a generic JSON format for your own systems, or a Slack format for a Slack incoming-webhook URL.

Webhook request

Request
POST <your URL>
Content-Type: application/json
X-Webhook-Event: HIGH_RISK_DETECTED
X-Webhook-Signature: <hex HMAC-SHA256 of the raw body>
Body
{
  "event": "HIGH_RISK_DETECTED",
  "data": {
    "assessmentId": "RISK-...",
    "userId": "user_123",
    "riskScore": 87,
    "riskLevel": "CRITICAL",
    "severity": "CRITICAL",
    "alertType": "HIGH_RISK_TRANSACTION",
    "description": "Risk score 87/100 — decision: BLOCK",
    "decision": "BLOCK",
    "reasons": ["Transaction from new country: RU"],
    "reasonCodes": ["GEOGRAPHIC_ANOMALY"]
  },
  "timestamp": "2026-06-03T15:00:01.000Z",
  "webhookId": "..."
}

data may contain additional fields; ignore any you don't use.

Verifying signatures

The signature is an HMAC-SHA256 of the raw request body using your webhook secret. Compute it over the exact bytes you received, before parsing the JSON.

Node.js
const crypto = require('crypto');

// Express: app.post('/archea', express.raw({ type: 'application/json' }), handler)
function isValid(rawBody, signatureHeader, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader || ''));
}

Delivery and retries

  • Respond with any 2xx within 5 seconds. Anything else counts as a failure.
  • Failed deliveries are tried 3 times in total: once immediately, then again after about 5 seconds and about 25 seconds.
  • After 10 consecutive failures, the webhook is automatically disabled. Re-enable it in the dashboard once your endpoint is fixed.
  • Use the Test button in the dashboard to send a TEST delivery.

Support

Need help integrating? Email support.archeaone@cycloudforte.com.