Before you start you need:
crk_....crk_... partner API key secret. It grants full access to your account's routing, transaction, and webhook data.
ClearRoute routes transactions across the PSPs you configure. Add at least one before making your first routing call.
Go to /onboarding and complete Step 2 — "Add PSP credentials". The UI guides you through adding Stripe or Adyen keys and validates them against the live API before saving.
POST to /api/onboarding/psp-keys with a JWT session token (obtained from POST /api/auth/login):
curl -X POST https://myclearroute.polsia.app/api/onboarding/psp-keys \
-H "Authorization: Bearer <your-jwt-token>" \
-H "Content-Type: application/json" \
-d '{
"provider": "stripe",
"api_key": "sk_live_...",
"fee_percent": 2.9
}'
Accepted values for provider: stripe or adyen. fee_percent is optional (defaults to the PSP's standard rate).
Validate credentials without saving them first using POST /api/onboarding/validate-psp with the same body shape — it returns { "valid": true } or an error message.
All partner API endpoints (/v1/*) authenticate via the X-Partner-API-Key header. Pass your crk_... key on every request:
curl https://myclearroute.polsia.app/v1/health \
-H "X-Partner-API-Key: crk_your_key_here"
A successful response looks like:
{
"status": "ok",
"timestamp": "2026-07-04T12:00:00.000Z"
}
If the header is missing or the key is invalid you will receive a 401 Unauthorized response.
Send a POST /v1/route request with the payment amount. ClearRoute scores all your configured PSPs on cost, latency, and historical success rate, then charges through the winner.
curl -X POST https://myclearroute.polsia.app/v1/route \
-H "X-Partner-API-Key: crk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 4999,
"currency": "usd",
"idempotency_key": "shop-order-12345",
"email": "customer@example.com"
}'
amount_cents — required, integer, minimum 50 (= $0.50).currency — optional ISO 4217 code, defaults to usd.idempotency_key — recommended; re-submitting the same key within 24 h returns the cached result without re-charging (idempotent_hit: true).email — optional customer email for receipts.{
"transaction": {
"id": "txn_01J2K...",
"amount_cents": 4999,
"currency": "usd",
"outcome": "success",
"psp_name": "Adyen",
"psp_fee_cents": 145,
"latency_ms": 312,
"created_at": "2026-07-04T12:01:22.000Z"
},
"routing": {
"winner": "Adyen",
"winner_id": 3,
"reason": "Adyen ranked #1 — lowest blended cost (fee 1.45%) and highest 30d success rate (99.2%)",
"scores": [
{ "psp_id": 3, "name": "Adyen", "score": 0.91 },
{ "psp_id": 1, "name": "Stripe", "score": 0.62 }
]
}
}
Key fields to inspect:
routing.winner — which PSP was used.routing.reason — human-readable explanation of the routing decision.transaction.outcome — success, declined, or failed.routing.reason field will note when a fallback occurred.
ClearRoute can POST real-time transaction events to any HTTPS URL you register.
Go to Dashboard → Webhooks and add your endpoint URL, or call POST /api/webhooks/partner-urls with a JWT token. ClearRoute delivers events with a short exponential-backoff retry (up to 3 attempts) if your endpoint does not return a 2xx response.
| Event | When it fires |
|---|---|
transaction.success | Payment authorised and captured by the PSP |
transaction.declined | PSP declined the charge (insufficient funds, card blocked, etc.) |
transaction.failed | All PSP attempts exhausted without a response |
psp.failure | A PSP returned an unexpected error (network timeout, API outage) |
{
"event_type": "transaction.success",
"transaction_id": "txn_01J2K...",
"timestamp": "2026-07-04T12:01:22.000Z",
"data": {
"amount_cents": 4999,
"currency": "usd",
"psp_name": "Adyen",
"outcome": "success"
}
}
Every delivery includes an X-ClearRoute-Signature header containing an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret. Always verify it before processing:
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post('/webhooks/clearroute', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-clearroute-signature'];
if (!verifyWebhook(req.body, sig, process.env.CLEARROUTE_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Handle event.event_type ...
res.status(200).json({ received: true });
});
Machine-readable error codes are returned in the code field of error responses alongside a human-readable error message.
| Code | HTTP status | Meaning |
|---|---|---|
PSP_ALL_FAILED | 502 | All configured PSPs declined or errored. Check your PSP dashboard for details. |
PSP_NOT_CONFIGURED | 422 | No PSPs configured, or none have valid API keys. Add a PSP via the onboarding UI or API. |
INTERNAL_ERROR | 500 | Unexpected error. Retry with exponential backoff; contact support if it persists. |
NOT_FOUND | 404 | The requested resource does not exist. |
openapi.yaml in the ClearRoute repository, covering all request/response schemas, error shapes, and rate-limit details.