NeverDown API

NeverDown is a drop-in replacement for the Stripe API. Same endpoints, same request params, same object shapes, same webhook signature scheme. Behind that surface every charge is scored and routed across all of your processor accounts, with automatic failover when one degrades or freezes.

- const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
+ const stripe = new Stripe(process.env.NEVERDOWN_SECRET_KEY, {
+   host: "router.neverdown.ai",
+   protocol: "https",
+ });

Base URL: https://router.neverdown.ai/v1 (also served at https://router.neverdown.ai/api/public/v1). All requests accept application/x-www-form-urlencoded or JSON, and all responses are JSON.

Quickstart

Create a key under Developers in your console, then charge a card:

curl https://router.neverdown.ai/v1/payment_intents \
  -u nd_test_yourkey: \
  -H "Idempotency-Key: order_1041" \
  -d amount=4999 \
  -d currency=usd \
  -d confirm=true \
  -d "payment_method_data[type]=card" \
  -d "payment_method_data[card][number]=4242424242424242" \
  -d "payment_method_data[card][exp_month]=12" \
  -d "payment_method_data[card][exp_year]=2030" \
  -d "payment_method_data[card][cvc]=123"
{
  "id": "pi_...",
  "object": "payment_intent",
  "amount": 4999,
  "currency": "usd",
  "status": "succeeded",
  "latest_charge": "ch_...",
  "neverdown": {
    "processor": "stripe",
    "routing_score": 0.87,
    "attempts": [{ "account": "Stripe — Primary", "status": "approved", "latency_ms": 412 }]
  }
}

Authentication

Keys look like nd_test_… and nd_live_…. Send them as a bearer token or as the HTTP Basic username (what Stripe SDKs do automatically). Keys are hashed at rest and shown once at creation. Rate limit: 300 requests per minute per key.

Authorization: Bearer nd_live_xxx
Idempotency-Key: <your unique key>   # POST requests, replayed for 24h

Payment Intents

POST/v1/payment_intentscreate (optionally confirm)
GET/v1/payment_intents/:idretrieve
POST/v1/payment_intents/:idupdate
POST/v1/payment_intents/:id/confirmauthorize & route
POST/v1/payment_intents/:id/capturemanual capture
POST/v1/payment_intents/:id/cancelcancel
GET/v1/payment_intentslist

Supported params: amount, currency, confirm, customer, payment_method, payment_method_data[card][…], capture_method, description, receipt_email, statement_descriptor, metadata[…], off_session.

Charges

POST/v1/chargeslegacy one-step charge
GET/v1/charges/:idretrieve
GET/v1/chargeslist (filter by payment_intent)
POST/v1/charges/:id/refundlegacy refund
curl https://router.neverdown.ai/v1/charges \
  -u nd_live_yourkey: \
  -d amount=2500 -d currency=usd -d source=tok_...

Refunds

POST/v1/refundscharge or payment_intent, full or partial
GET/v1/refunds/:idretrieve
GET/v1/refundslist

Customers & payment methods

POST/v1/customerscreate
GET/v1/customers/:idretrieve
POST/v1/customers/:idupdate
DELETE/v1/customers/:iddelete
POST/v1/payment_methodstokenize a card
POST/v1/payment_methods/:id/attachattach to customer
POST/v1/payment_methods/:id/detachdetach
GET/v1/payment_methodslist by customer
POST/v1/tokenslegacy card token

Card vault & PCI

Card numbers you send are encrypted with AES-256-GCM under a versioned server-held key and stored in a vault table that no client, dashboard, or API key can read. Only the last four digits, brand, BIN range, expiry, and a merchant-scoped fingerprint are exposed in API responses. The full number is decrypted for a few milliseconds inside the authorization path and never logged. Re-charging uses the reusable pm_… token, so a card entered once works across every processor NeverDown routes to.

Handling raw card data on your servers places you in PCI DSS scope (SAQ D). Send cards over TLS only, and keep them out of your own logs.

Webhooks

Register endpoints in the console or over the API. Deliveries carry both NeverDown-Signature and Stripe-Signature headers in the identical t=…,v1=… HMAC-SHA256 format, so existing verification code keeps working — just swap in the NeverDown endpoint secret. Failed deliveries retry three times.

const event = stripe.webhooks.constructEvent(
  rawBody,
  req.headers["stripe-signature"],
  process.env.NEVERDOWN_WEBHOOK_SECRET,
);

Event types: payment_intent.succeeded, payment_intent.payment_failed, payment_intent.canceled, charge.succeeded, charge.failed, charge.captured, charge.refunded, customer.created, payment_method.attached, payment_method.detached.

Errors & decline codes

Errors use the Stripe envelope and status codes: 400 invalid request, 401 bad key, 402 card declined, 404 missing resource, 429 rate limited.

{
  "error": {
    "type": "card_error",
    "code": "card_declined",
    "decline_code": "insufficient_funds",
    "message": "Your card has insufficient funds.",
    "payment_intent": { "id": "pi_...", "status": "requires_payment_method" }
  }
}

Every processor's native decline reason is normalised to a canonical NeverDown reason first, then mapped to the matching Stripe decline code, so your existing retry logic behaves the same no matter which processor handled the attempt.

Routing metadata

Every PaymentIntent and Charge carries an extra neverdown object — ignore it and the response is Stripe-shaped, or read it to see which processor won, the routing score, and each failover attempt.

"neverdown": {
  "processor": "authorize_net",
  "processor_account_id": "…",
  "routing_decision_id": "…",
  "routing_score": 0.74,
  "attempts": [
    { "account": "Stripe — Primary", "status": "error", "reason": "processor_unavailable", "latency_ms": 2100 },
    { "account": "Authorize.Net", "status": "approved", "reason": null, "latency_ms": 380 }
  ]
}

Test cards

NumberBehaviour
4242 4242 4242 4242Approves
4000 0000 0000 0002Generic decline
4000 0000 0000 9995Insufficient funds
4000 0000 0000 0069Expired card
4000 0000 0000 0127Incorrect CVC
4000 0000 0000 0119Processing error — triggers failover

Compatibility matrix

Stripe featureNeverDown
PaymentIntents (create, confirm, capture, cancel)Supported
Charges + legacy refundsSupported
Refunds (full and partial)Supported
Customers, PaymentMethods, TokensSupported
Events + signed webhooksSupported
Idempotency keysSupported
Stripe.js / ElementsNot yet — post card data server-side
3D Secure / SCANot yet
Subscriptions, Invoices, ConnectNot planned

Ready to switch?

Create an account, add your processors, and mint a test key in a couple of minutes.

Get started