{
  "schema_version": "2.36.0",
  "name": "XR-Sentinel",
  "provider": "XRPL-Utilities\u2122",
  "description": "AI-assisted behavioral pattern classifier for XRP Ledger (XRPL) wallets. Describes how automated and service-like a wallet's recent activity appears. Not a risk, compliance, or sanctions service.",
  "contact": "hello@xrpl-utilities.com",
  "getting_started": "Before your first /scan, GET /schema once and cache it. The schema document enumerates the signal catalog, feature keys, confidence tiers, and activity levels that scan responses reference by shorthand. Watch the top-level schema_version field to detect catalog changes and refresh your cache.",
  "location": "Cloud-Native / Global",
  "data_residency": "agnostic",
  "pii": "none",
  "capabilities": {
    "classification": "activity_pattern",
    "window_days": 90,
    "analysis_depth_tx": 200,
    "outputs": [
      "activity_score_0_100",
      "institutional_posture_0_100_with_tier_and_per_signal_contributors",
      "activity_level_low_medium_high_dormant_unknown",
      "confidence_low_medium_high",
      "signals_array",
      "features_object_including_labeled_top_counterparties",
      "features_include_trustset_amm_nft_op_counts",
      "features_include_failed_tx_ratio_and_median_seconds_between_tx",
      "features_include_currency_net_flows_per_asset",
      "features_include_issuer_summary_when_target_is_an_issuer",
      "delta_block_when_prior_recorded_scan_exists",
      "second_order_signals_score_trajectory_cadence_counterparty_burst",
      "narrative_reasoning",
      "ai_narrative_provenance_block_model_configured_model_resolved_prompt_version_temperature_set_response_format",
      "did_brief_when_wallet_published_xls40_did_with_org_name_principals_description_source_toml_url"
    ],
    "signals_documented_at": "/schema",
    "external_label_source": "api.xrpscan.com (best-effort, cached, graceful on failure)",
    "not_provided": [
      "risk_scoring",
      "sanctions_screening",
      "aml_compliance",
      "graph_forensics"
    ],
    "bulk_scan": {
      "endpoints": {
        "quote": "/bulk/quote",
        "status": "/bulk/status/{invoice_id}",
        "results": "/bulk/results/{invoice_id}"
      },
      "min_addresses": 1,
      "max_addresses": 50,
      "price_per_address_usd": "0.10",
      "shape": "async invoice flow: POST /bulk/quote returns 402 with PaymentRequirements + invoice_id, payer settles via x402, server polls for tagged inbound payment, runs the N scans in-process at the same shape as /scan, exposes status via /bulk/status/{id} and results via /bulk/results/{id}.",
      "dedup_policy": "duplicate addresses in a single call are deduped pre-quote and not double-billed",
      "timing": "each scan ~30s; 50-address invoice typically completes within 25-30 minutes of payment.",
      "flow_rationale": "synchronous would block the client for tens of minutes; async-with-polling matches how agents already poll x402 settlement state.",
      "rate_limits": {
        "quote_per_minute_per_ip": 4,
        "quote_keying_note": "Per-IP. Per-payer keying isn't viable because the payer wallet isn't visible at /bulk/quote time; settlement (and payer identity) happens later via the t54 facilitator + destination-tag binding. Tightened from the /scan default of 15/min/IP because each successful quote mints persistent invoice state.",
        "status_per_minute_per_ip": 15,
        "results_per_minute_per_ip": 15,
        "concurrency_note": "Each paid invoice runs its N scans sequentially in one in-process job; there is no per-payer parallelism cap because there is no per-payer parallelism. A single invoice cannot cause more than one scan in flight from the bulk path itself.",
        "retry_after_seconds_on_429": 60,
        "addresses_per_quote_max": 50,
        "max_addresses_per_minute_per_ip": 200
      }
    },
    "origin_verification": {
      "endpoint": "/verify-origin",
      "price": "free",
      "question": "does this web origin actually control the XRPL accounts it advertises, and is the payout address in its x402 catalog one the domain itself declares?",
      "input": "url (http/https origin; private, loopback and link-local targets refused)",
      "bindings_checked": [
        "TOML_SERVED \u2014 domain publishes .well-known/xrp-ledger.toml (XLS-26)",
        "TOML_DECLARES_ACCOUNT \u2014 that toml names mainnet [[ACCOUNTS]]",
        "DOMAIN_ROUNDTRIP \u2014 a declared account also sets a Domain field pointing back; two-way, not one party's self-assertion",
        "DID_PUBLISHED \u2014 XLS-40 did.json resolves, and whether it carries an actual verification key or is a placeholder",
        "DID_ACCOUNT_DECLARED \u2014 the account the DID names is one the toml declares",
        "PAYTO_DECLARED \u2014 every payTo in the served x402 catalog is declared by the domain; the check a swapped catalog fails",
        "PAYTO_CREDENTIAL_BOUND \u2014 an on-ledger XLS-70 credential binds the payout address to a declared identity account",
        "MPT_BRAND_UNATTESTED \u2014 an MPTokenIssuance whose metadata names an organisation with no verified domain behind the account"
      ],
      "verdicts": [
        "verified",
        "partial",
        "unverified",
        "unreadable",
        "mismatch"
      ],
      "evidence_tier": [
        "toml",
        "toml-oneway",
        "none"
      ],
      "unreadable_is_not_absent": "a document that could not be fetched returns UNREADABLE and never a negative finding",
      "not_provided": [
        "endorsement of the operator, product or solvency",
        "proof of key custody",
        "anything about conduct"
      ]
    }
  },
  "payment": {
    "spec": "x402",
    "x402_version": 2,
    "scheme": "exact",
    "amount_usd": "0.10",
    "currencies_accepted": [
      "XRP",
      "RLUSD",
      "USDC"
    ],
    "rails": [
      {
        "network": "xrpl:0",
        "currency": "XRP",
        "recipient": "rKxTzCKYKPPdXEzuioEQ6KekQK26w2DBd5",
        "amount_usd": "0.10",
        "note": "XRP amount is dynamic - equivalent to $0.10 USD at current spot. Exact required drops are advertised per-request in the 402 PaymentRequirements body."
      },
      {
        "network": "xrpl:0",
        "currency": "RLUSD",
        "recipient": "rKxTzCKYKPPdXEzuioEQ6KekQK26w2DBd5",
        "amount": "0.10",
        "issuer": "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De",
        "currency_hex": "524C555344000000000000000000000000000000"
      },
      {
        "network": "eip155:8453",
        "currency": "USDC",
        "recipient": "0xADB77e932516298660C47e390676c2F053D7f3c8",
        "amount_atomic": "100000",
        "decimals": 6,
        "contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "note": "USDC-on-Base settled via the Coinbase x402 facilitator. Adds a Base-native rail for AgentCore clients whose Coinbase or Stripe wallet holds USDC on Base. Same $0.10 price as the XRPL rails."
      }
    ],
    "network": "xrpl:0",
    "recipient": "rKxTzCKYKPPdXEzuioEQ6KekQK26w2DBd5",
    "xrp": {
      "note": "XRP amount is dynamic - equivalent to $0.10 USD at current spot. Exact required drops are advertised per-request in the 402 PaymentRequirements body."
    },
    "rlusd": {
      "amount": "0.10",
      "issuer": "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De",
      "currency_hex": "524C555344000000000000000000000000000000"
    },
    "auth_header": "X-PAYMENT (standard x402; legacy PAYMENT-SIGNATURE also accepted)",
    "auth_value": "Base64(JSON) of an x402 v2 PaymentPayload. Standard clients send the baseline envelope `{x402Version, scheme, network, payload}` and are bound to a rail by scheme+network (add top-level `asset` to disambiguate the two XRPL rails); the rail-specific proof goes in `payload` (XRPL: presigned XRPL Payment hex in payload.signedTxBlob; EVM: signed EIP-3009 transferWithAuthorization in payload.{signature,authorization}). The legacy dialect that echoes `accepted` plus a server-issued payload.invoiceId is still accepted on the XRPL rails. The EVM accept carries its EIP-712 domain (name/version) plus the shared invoiceId (so a Base-only payer can bind to the same invoice as the XRPL rails); binding is still by scheme/network, not by invoiceId, and the invoiceId is stripped before the facilitator call.",
    "settlement_header": "X-PAYMENT-RESPONSE (also mirrored on legacy PAYMENT-RESPONSE)",
    "settlement_value": "Base64(JSON) of the facilitator settle response on success: {success, transaction, network, payer}.",
    "challenge_format": "x402-spec PaymentRequired body with accepts[]; mirrored base64 in PAYMENT-REQUIRED header.",
    "freshness_seconds": 600,
    "scope": "single use per payment. Standard clients are replay-protected by the facilitator (EIP-3009 nonce on EVM, tx hash on XRPL); the legacy dialect binds extra.invoiceId. Retrying a lost delivery: a baseline-envelope buyer may re-present the same X-PAYMENT within 60s and is served the already-paid body without being charged again. A retry carrying only the on-chain payment proof gets ONE re-delivery, because that proof is public once the payment settles; a further retry inside the window returns 402 replay_limit_reached. For unrestricted retries inside the window, send X-Replay-Token (returned on the settle response) or put an X-Idempotency-Key of at least 16 characters on the ORIGINAL request. The legacy accepted-echoing dialect keeps an uncapped 600s idempotency window, because its invoiceId is server-issued and never becomes public.",
    "client_sdk": "x402-xrpl (PyPI) for XRPL rails; the upstream x402 SDK (PyPI/npm) or any x402 v2 client for the USDC-on-Base rail.",
    "verify_before_settle": true,
    "refund_policy": "Verify-before-settle - if the AI step fails (HTTP 503), settlement is skipped. Successful classifications and cached responses are paid; no refunds otherwise.",
    "scope_notes": "Multi-rail applies to /scan and /scan/history. /bulk/quote remains XRPL-only because settlement is bound to an XRPL DestinationTag rather than the inline x402 verify/settle flow."
  },
  "trust_paths": {
    "paths": [
      {
        "name": "paid_x402",
        "trigger": "X-PAYMENT or PAYMENT-SIGNATURE header (x402 v2 PaymentPayload)",
        "served_via": null,
        "billed": true,
        "settlement_header": "PAYMENT-RESPONSE",
        "audience": "production agents that hold a wallet and can sign x402 payments"
      },
      {
        "name": "sister_product",
        "trigger": "X-Sister-Product-Key header (HMAC-compared against an operator-issued shared secret)",
        "served_via": "sister_product",
        "billed": false,
        "settlement_header": null,
        "audience": "sibling XR-* services calling Sentinel from their own backend (currently XR-Pulse via sentinel_lookup.py)"
      },
      {
        "name": "preview_token",
        "trigger": "Authorization: Bearer <JWT> minted at xrpl-utilities.com/api/preview-token via Cloudflare Turnstile",
        "served_via": "preview-token",
        "billed": false,
        "settlement_header": null,
        "audience": "browsers on the marketing site running a live preview from the operator's free quota"
      }
    ],
    "parity_guarantees": {
      "response_shape": "identical PatternReport top-level key set across all three paths; only served_via and the presence of the PAYMENT-RESPONSE settlement header differ. Asserted by tests/test_handler.py::test_served_via_path_parity.",
      "rate_limit": "shared per-client-fingerprint bucket of MAX_REQUESTS_PER_MINUTE applies BEFORE any auth check, so no trust path can exceed the documented rate budget. Asserted by tests/test_handler.py::test_rate_limit_applies_uniformly_across_trust_paths.",
      "idempotency_cache": "keyed by the x402 invoice_id AND a server-side binding over the service, the endpoint, the validated request and the presented payment proof - the invoice id alone never unlocks a paid body. Only the paid x402 path populates it. Free paths re-run the live work on each call (deterministic given the cache.py per-address scan cache, which is path-agnostic)."
    },
    "operator_paths_not_listed": "An operator-only smoke-test path exists for internal CI; it is not promised to callers and not part of the parity surface."
  },
  "mcp_capability": {
    "hosted_endpoint": "https://mcp.xrpl-utilities.io",
    "stdio_command": "npx -y @xrpl-utilities/mcp",
    "npm_package": "@xrpl-utilities/mcp",
    "github_repo": "https://github.com/XRPL-Utilities/xrpl-utilities-mcp",
    "tools": [
      "xrpl_sentinel_scan",
      "xrpl_sentinel_scan_history"
    ],
    "note": "Same read endpoints are also exposed as MCP tools for AI clients (Claude Desktop, Cursor, anything Model Context Protocol-aware). Stateless passthrough - the MCP server forwards the caller's own x402 payment header to the backend; no billing aggregation."
  },
  "destination_tag_partition": {
    "scheme": "32-bit XRPL DestinationTag; top 8 bits encode the service that minted the tag.",
    "service_prefix_hex": "0x01",
    "service_prefix_int": 1,
    "tag_range_inclusive": [
      16777216,
      33554431
    ],
    "tag_range_hex": [
      "0x01000000",
      "0x01ffffff"
    ],
    "decoder_pseudocode": "service_id = (destination_tag >> 24) & 0xFF",
    "portfolio_prefix_map": {
      "0x01": "Sentinel",
      "0x02": "Pulse (reserved, not yet minting tags)",
      "0x03": "Telemetry",
      "0x04": "Trust (reserved, not yet minting tags)",
      "0x05": "Vault (reserved, not yet minting tags)",
      "0x06": "Flows (reserved, not yet minting tags)"
    },
    "note": "Lets operators and paying agents reconcile incoming Payments at the shared treasury wallet by service without correlating with each service's invoice store. x402 inline payments (on /scan) use the invoiceId field in the payment payload instead of DestinationTag; this partition applies only to invoice-flow tags returned in /quote (Sentinel /bulk/quote, Telemetry /quote)."
  },
  "endpoints": {
    "base_url": "https://sentinel.xrpl-utilities.io",
    "discovery": "/llms.txt",
    "schema": "/schema",
    "tos": "/tos",
    "scan": "/scan",
    "scan_history": "/scan/history",
    "verify_origin": "/verify-origin?url={origin_url}",
    "bulk_quote": "/bulk/quote",
    "bulk_status": "/bulk/status/{invoice_id}",
    "bulk_results": "/bulk/results/{invoice_id}",
    "manifest": "/agents.json",
    "health": "/healthz",
    "stats": "/stats",
    "docs": "/docs",
    "openapi": "/openapi.json",
    "sla": "/sla?days=30"
  },
  "rate_limits": {
    "requests_per_minute": 15,
    "cache_ttl_seconds": 86400
  },
  "legal": {
    "notice": "BEHAVIORAL PATTERN CLASSIFICATION ONLY. Not a risk, compliance, AML, or sanctions determination. Not financial or legal advice. Always do your own research and independently verify any address before transacting based on these results.",
    "user_responsibility": "XRPL-Utilities provides behavioral data classification and technical ledger analysis. We do not provide financial advice, risk ratings, or investment forensics. Users are responsible for ensuring their use of these tools complies with local digital asset regulations, including the California Digital Financial Assets Law and the Colorado AI Act where applicable. This service is offered void where prohibited; use is not authorized in California or Colorado."
  }
}
