Skip to content

API Reference

Base URL: http://localhost:8080

All endpoints under /v1/ require API key authentication when PULSEROUTE_API_KEYS is set. Pass the key via X-API-Key header. The /metrics endpoint is always public.


Routing

POST /v1/route

Get a routing decision for a transaction. Call this before processing the payment.

Request:

{
  "country": "US",
  "currency": "USD",
  "payment_method": "card",
  "card_type": "visa",
  "transaction_type": "one_time",
  "amount": 99.99,
  "amount_tier": null
}
Field Type Required Default Description
country string yes ISO 3166-1 alpha-2
currency string yes ISO 4217
payment_method string no card card, bank_transfer, wallet, bnpl
card_type string no * visa, mastercard, amex, etc.
transaction_type string no one_time one_time, recurring, subscription
amount float no null Transaction amount
amount_tier string no auto micro, standard, high_value, premium. Derived from amount if omitted.

Response (200):

{
  "processor_id": "stripe",
  "weight": 0.95,
  "fallback_processor_id": "adyen",
  "decision_source": "mab",
  "failure_probability": 0.02
}
Field Description
processor_id Recommended processor
weight Traffic allocation weight (0-1)
fallback_processor_id Secondary processor if primary fails
decision_source rules (Tier 1), prediction (Tier 2), or mab (Tier 3)
failure_probability Tier 2 LSTM prediction (null if Tier 2 disabled)

Errors: 404 if no matching rule exists.


POST /v1/outcome

Report a transaction outcome. Call this after the payment completes.

Request:

{
  "rule_id": "us_cards",
  "processor_id": "stripe",
  "success": true,
  "latency_ms": 120.0,
  "error_code": null
}
Field Type Required Description
rule_id string yes Routing rule that was used
processor_id string yes Processor that handled the transaction
success bool yes Whether the transaction succeeded
latency_ms float yes End-to-end latency in milliseconds
error_code string no Processor error code (if failed)

Response (202):

{
  "accepted": true
}

Rules

GET /v1/rules

List all routing rules.

Response (200):

[
  {
    "rule_id": "us_cards",
    "primary": {"processor_id": "stripe", "name": "Stripe"},
    "secondary": {"processor_id": "adyen", "name": "Adyen"},
    "primary_weight": 0.9,
    "secondary_weight": 0.1
  }
]

POST /v1/rules

Add a routing rule.

Request:

{
  "rule_id": "us_cards",
  "attributes": ["US", "USD", "card", "*", "*", "*"],
  "primary_id": "stripe",
  "primary_name": "Stripe",
  "secondary_id": "adyen",
  "secondary_name": "Adyen",
  "primary_weight": 0.9
}

Attribute padding

Attribute lists shorter than 6 are padded with * (wildcard). So ["US", "USD", "*"] is equivalent to ["US", "USD", "*", "*", "*", "*"].

Response (201): Returns the created rule.

DELETE /v1/rules/{rule_id}

Remove a routing rule.

Response (200): {"deleted": "us_cards"} Errors: 404 if rule not found.


Health & Monitoring

GET /v1/health

Overall engine health with per-rule breakdown.

Response (200):

{
  "status": "ok",
  "engine_running": true,
  "tier": 3,
  "rules": {
    "us_cards": {
      "rule_id": "us_cards",
      "primary": {
        "processor_id": "stripe",
        "status": "healthy",
        "success_rate": 0.98,
        "error_rate": 0.02,
        "avg_latency_ms": 115.0,
        "p95_latency_ms": 180.0,
        "p99_latency_ms": 220.0,
        "sample_count": 1500,
        "failure_probability": 0.03
      },
      "secondary": { "..." : "..." },
      "decision": {
        "processor_id": "stripe",
        "weight": 0.95,
        "decision_source": "mab"
      }
    }
  }
}

GET /v1/health/{rule_id}

Health for a specific rule.

Errors: 404 if rule not found.

GET /v1/stats

Engine statistics.

Response (200):

{
  "transactions_routed": 15000,
  "transactions_reported": 14800,
  "failover_events": 3,
  "running": true,
  "shadow_mode": false,
  "tier": 3,
  "trie": {"total_nodes": 15, "total_rules": 5},
  "bandit": {"rule_count": 5}
}

GET /v1/failover-events

List failover events (audit trail).

Response (200):

[
  {
    "rule_id": "us_cards",
    "from_processor": "stripe",
    "to_processor": "adyen",
    "reason": "Primary error rate 35.0% exceeds threshold 30.0%",
    "trigger_value": 0.35,
    "threshold": 0.30,
    "timestamp": "2026-04-01T12:00:00"
  }
]

GET /v1/shadow-report

Shadow mode comparison report showing what PulseRoute would have done differently.

Query Parameters:

Parameter Type Default Description
average_order_value float 50.0 Customer AOV for revenue impact estimate

Response (200):

{
  "shadow_mode_active": true,
  "observation_period_seconds": 3600.0,
  "total_transactions": 500,
  "total_outcomes": 480,
  "routing_overrides": 45,
  "failover_events_detected": 2,
  "transactions_on_degraded_processor": 30,
  "estimated_saved_transactions": 12,
  "estimated_revenue_impact": 600.0,
  "rules": {
    "us_cards": {
      "total_outcomes": 480,
      "failures": 18,
      "overrides": 45,
      "failures_on_degraded": 12,
      "recommended_processor": "adyen"
    }
  },
  "earliest_observation": "2026-04-01T10:00:00+00:00",
  "latest_observation": "2026-04-01T11:00:00+00:00"
}

Key metrics explained: - routing_overrides — transactions where PulseRoute would have used a different processor than the customer's system chose - estimated_saved_transactions — failures on processors PulseRoute flagged as degraded (these would have been avoided) - estimated_revenue_impactestimated_saved_transactions × average_order_value


Configuration

GET /v1/config/tiers

Get current tier configuration.

POST /v1/config/tiers

Toggle tiers at runtime.

Request:

{
  "prediction_enabled": true,
  "bandit_enabled": false
}

Onboarding

POST /v1/onboard

Bulk setup: define processor capabilities and auto-generate routing rules. This is the API behind the dashboard's onboarding wizard.

Request:

{
  "processors": [
    {
      "processor_id": "stripe",
      "name": "Stripe",
      "countries": ["US", "GB"],
      "currencies": ["USD", "GBP"],
      "payment_methods": ["card", "wallet"],
      "priority": 1
    },
    {
      "processor_id": "adyen",
      "name": "Adyen",
      "countries": ["US", "GB", "DE"],
      "currencies": ["USD", "GBP", "EUR"],
      "payment_methods": ["card"],
      "priority": 2
    }
  ],
  "primary_weight": 0.9
}
Field Type Required Default Description
processors array Yes At least 2 processors
processors[].processor_id string Yes Unique processor ID
processors[].name string Yes Display name
processors[].countries array Yes ISO 3166-1 country codes
processors[].currencies array Yes ISO 4217 currency codes
processors[].payment_methods array No ["card"] Payment method types
processors[].priority int No 1 Lower = higher priority
primary_weight float No 0.9 Traffic weight for primary (0.5-1.0)

Response (201):

{
  "rules_created": 4,
  "rules": [
    {
      "rule_id": "gb_gbp_card",
      "attributes": ["GB", "GBP", "card"],
      "primary_id": "stripe",
      "primary_name": "Stripe",
      "secondary_id": "adyen",
      "secondary_name": "Adyen",
      "primary_weight": 0.9
    }
  ],
  "processors": ["stripe", "adyen"]
}

For each (country, currency, payment_method) combination where processors overlap, a routing rule is created with the highest-priority processor as primary and the next as secondary. Combinations served by only one processor get a self-fallback rule.


Webhooks

POST /v1/webhooks

Register a webhook endpoint.

Request:

{
  "url": "https://your-service.com/webhook",
  "event_types": ["failover.triggered", "failover.recovered"],
  "secret": "your-hmac-secret"
}

Event types:

Event Fired when
failover.triggered Traffic fails over from primary to secondary
failover.recovered Traffic returns to primary after recovery
processor.degraded Processor status changes to degraded
processor.healthy Processor status returns to healthy

Response (201):

{
  "id": "ab02e80d58cb",
  "url": "https://your-service.com/webhook",
  "event_types": ["failover.triggered", "failover.recovered"],
  "active": true,
  "created_at": "2026-04-01T12:00:00"
}

GET /v1/webhooks

List all webhooks.

GET /v1/webhooks/{webhook_id}

Get a specific webhook.

DELETE /v1/webhooks/{webhook_id}

Remove a webhook.


Webhook Delivery

Webhook payloads are delivered via HTTP POST with:

  • Content-Type: application/json
  • X-PulseRoute-Signature: sha256=<hmac> (if secret is set)
  • Retry: 3 attempts with exponential backoff
  • Timeout: 10 seconds per attempt

Payload:

{
  "event_id": "evt_abc123",
  "event_type": "failover.triggered",
  "timestamp": "2026-04-01T12:00:00",
  "data": {
    "rule_id": "us_cards",
    "from_processor": "stripe",
    "to_processor": "adyen"
  }
}

Verifying signatures (Python):

import hashlib, hmac

def verify_signature(body: bytes, secret: str, signature: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

Prometheus Metrics

Endpoint: GET /metrics/ (no auth required)

Metric Type Description
pulseroute_requests_total Counter HTTP requests by method, path, status
pulseroute_request_duration_seconds Histogram Request latency by method, path
pulseroute_transactions_routed_total Counter Total routing decisions
pulseroute_transactions_reported_total Counter Total outcomes reported
pulseroute_failover_events_total Counter Failover events by rule
pulseroute_processor_health_status Gauge 1=healthy, 0.5=degraded, 0=failed_over
pulseroute_processor_success_rate Gauge Current success rate
pulseroute_processor_latency_p95 Gauge P95 latency in ms
pulseroute_processor_failure_probability Gauge Tier 2 LSTM prediction
pulseroute_bandit_weight Gauge Tier 3 traffic allocation
pulseroute_engine_tier Gauge Current engine tier (1/2/3)
pulseroute_redis_connected Gauge Redis connectivity (0/1)

Authentication & Multi-Tenancy

API keys authenticate requests and determine tenant scope. Set PULSEROUTE_API_KEYS with key:tenant_id pairs:

# Multi-tenant
PULSEROUTE_API_KEYS=key1:acme,key2:beta,key3:acme

# Legacy (all keys map to "default" tenant)
PULSEROUTE_API_KEYS=key1,key2,key3

Pass the key in requests:

curl -H "X-API-Key: key1" http://localhost:8080/v1/health

Each tenant has fully isolated routing rules, metrics, health state, and webhooks. When PULSEROUTE_API_KEYS is not set, authentication is disabled (dev mode, all requests use the "default" tenant).


Tenants

GET /v1/tenants

List all active tenants.

Response (200):

{
  "tenants": [
    {
      "tenant_id": "acme",
      "active": true,
      "rules_count": 5,
      "engine_running": true
    }
  ],
  "total": 1
}

GET /v1/tenants/{tenant_id}

Get details for a specific tenant.

Response (200):

{
  "tenant_id": "acme",
  "active": true,
  "rules_count": 5,
  "engine_running": true,
  "tier": 3,
  "transactions_routed": 15000,
  "transactions_reported": 14800,
  "failover_events": 3
}

Errors: 404 if tenant not found.


OpenAPI / Swagger

FastAPI auto-generates interactive API docs:

  • Swagger UI: http://localhost:8080/docs
  • ReDoc: http://localhost:8080/redoc