Skip to content

Fraud Signals

One risk score from SIM-swap recency, BVN demographic changes and watchlist status. Built for account-takeover and onboarding fraud.

View as Markdown

Combines SIM-swap recency, recent BVN demographic changes and the industry watchlist into one risk score from 0 to 100. Built for account takeover and onboarding fraud.

POST/v1/signals
Availability
Sandbox and live
Indicative price
₦200.00 per completed check
Consent
Your attestation (client_attested).
MCP tool
check_signals

Request

Body parameters

  • phonestring

    Format: Nigerian mobile number, 0803… or +234803….

  • bvnstring

    Format: 11 digits.

  • reasonstringrequired

    Purpose of the check. One of the 38 NIMC purpose codes. See the purpose code list.

  • consentobjectrequired

    Your attestation that you obtained the data subject's consent for this purpose.

  • consent.obtainedbooleanrequired

    Always true.

  • Provide phone, bvn, or both. The phone drives SIM-swap signals; the BVN drives demographic changes and the watchlist.
  • match is always null: a risk score is not a match. Read summary.risk_score and summary.risk_level.
  • Weights: watchlisted 60, SIM swap within 7 days 35 (within 30 days 15), BVN name change within 30 days 20, BVN phone change within 30 days 10. The score is capped at 100; high from 60, medium from 30.

Send an Idempotency-Key header so a retried request is never billed twice. See Idempotency.

curl -X POST http://localhost:4000/v1/signals \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "08099990000",
  "bvn": "22222222225",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  }
}'

Response

A completed check returns 200 with a verification object. status is verified or not_found. If the provider has not answered in time you get 202 with status: "pending"; retrieve it later or wait for the verification.completed webhook.

200 OK · assertion
{
  "reference_id": "spm_8171f3a87cbe221341f7185f",
  "object": "verification",
  "product": "signals",
  "method": "risk_signals",
  "status": "verified",
  "match": null,
  "field_matches": null,
  "field_confidence": null,
  "mode": "sandbox",
  "reason": "financialProducts",
  "subject": "*******2225",
  "summary": {
    "risk_score": 100,
    "risk_level": "high",
    "factors": [
      {
        "code": "watchlisted",
        "weight": 60,
        "detail": "BVN is on the industry watchlist"
      },
      {
        "code": "sim_swap_7d",
        "weight": 35,
        "detail": "SIM swapped 2 day(s) ago"
      },
      {
        "code": "bvn_name_change_30d",
        "weight": 20,
        "detail": "Name changed on the BVN in the last 30 days"
      },
      {
        "code": "bvn_phone_change_30d",
        "weight": 10,
        "detail": "Phone number changed on the BVN in the last 30 days"
      }
    ],
    "sim_swap_recent": true,
    "days_since_sim_swap": 2,
    "bvn_demographic_changes": 2,
    "bvn_demographic_changes_30d": 2,
    "watchlisted": true
  },
  "consent": {
    "mechanism": "client_attested",
    "consent_id": null,
    "timestamp": "2026-10-19T09:41:07.512Z"
  },
  "source": "api",
  "error": null,
  "billing": {
    "amount_kobo": 0,
    "currency": "NGN"
  },
  "latency_ms": 520,
  "created_at": "2026-10-19T09:41:07.512Z",
  "updated_at": "2026-10-19T09:41:07.874Z"
}

summary

  • risk_scoreinteger

    Sum of factor weights, 0 to 100.

  • risk_levelstring

    low, medium or high.

  • factorsarray

    Each factor: code, weight and a readable detail.

  • sim_swap_recentboolean | null

    SIM swapped within 30 days. null without a phone.

  • days_since_sim_swapinteger | null

    Days since the last SIM swap.

  • bvn_demographic_changesinteger | null

    Number of demographic changes on the BVN. null without a BVN.

  • bvn_demographic_changes_30dinteger | null

    Changes in the last 30 days.

  • watchlistedboolean | null

    BVN is on the industry watchlist.

Full-tier accounts calling the REST API also receive provider, provider_reference and data, the normalized record. MCP, webhooks, lists and bulk results never include it. See Response shaping.

Full tier only
{
  "provider": "sandbox",
  "provider_reference": "SBX6CB753DB2C",
  "data": {
    "risk_score": 100,
    "risk_level": "high",
    "factors": [
      "…"
    ],
    "sim_swap": {
      "days_since_swap": 2
    },
    "bvn_changes": [
      "…"
    ]
  }
}

Test values

Send these bodies with a sandbox key. More outcome triggers are on the Sandbox page.

High risk
{
  "phone": "08099990000",
  "bvn": "22222222225",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  }
}
Medium risk (recent SIM swap)
{
  "phone": "08187654321",
  "bvn": "22222222226",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  }
}
Low risk
{
  "phone": "08031234567",
  "bvn": "22222222221",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  }
}

Errors

StatusCodeMeaning
400
invalid_request
The body failed validation. details lists each field.
401
unauthorized
Missing or invalid API key.
402
insufficient_balance
Live only: wallet balance is below the check price.
403
not_entitled
Your workspace is not enabled for this product or method.
409
idempotency_key_reused
The Idempotency-Key was used with a different body.
422
provider_rejected_request
The upstream registry rejected the request.
503
verification_unavailable
Temporary failure. Retry, with the same or a new Idempotency-Key.

All errors share one shape. See Errors for the full list.

Questions? Email developers@speedmap.ng. This page is also available as Markdown.