PayinferenceDocs
payinference.com

Getting started

This guide takes you from zero to your first decision, executed instruction, and reported outcome. It runs against a local PayInference instance. Everything here also applies to a hosted deployment once you swap the base URL and API key.

Prerequisites#

  • Node.js 20 or newer, pnpm 9
  • Docker with Compose v2 (Postgres 16 and Redis 7)

1. Run PayInference locally#

bash
pnpm install
cp .env.example .env
docker compose up -d
pnpm db:generate && pnpm db:migrate && pnpm db:seed
pnpm --filter @payinference/api dev

The Decision API listens on http://localhost:4000. The seed creates a demo workspace with merchant m_123 and a live-mode API key pi_demo_key_123 that carries every scope. See Local Setup for the full guide, ports, and troubleshooting.

2. Create your first decision#

Call the Decision API from a terminal. Note the two things every request needs: the x-api-key header and safe payment context only. Card numbers, CVVs, and emails are rejected by the schema, not ignored.

bash
curl -s http://localhost:4000/v1/decision \
  -H "content-type: application/json" \
  -H "x-api-key: pi_demo_key_123" \
  -d '{
    "merchant_id": "m_123",
    "order_id": "order_456",
    "transaction": {
      "amount": 14900,
      "currency": "USD",
      "country": "US",
      "payment_method": "card",
      "card_network": "visa",
      "customer_type": "returning"
    },
    "available_providers": ["stripe", "adyen", "paypal"]
  }'

The response contains exactly one instruction in action, plus the evidence behind it:

json
{
  "decision_id": "dec_9f3a1c0b22d14e55",
  "merchant_id": "m_123",
  "mode": "live",
  "action": "route",
  "route": { "primary_provider": "adyen", "fallback_provider": "stripe" },
  "risk": {
    "score": 0,
    "band": "low",
    "recommended_action": "route",
    "reason_codes": ["NORMAL_AMOUNT_FOR_MERCHANT", "RETURNING_CUSTOMER"]
  },
  "provider_health": {
    "adyen": { "score": 94, "state": "healthy", "confidence": 91, "source": "probe" },
    "stripe": { "score": 68, "state": "degraded", "confidence": 88, "source": "probe" }
  },
  "policy": {
    "policy_id": "policy_m_123_v1",
    "policy_version": "1.0.0",
    "matched_rules": ["avoid_degraded_providers"]
  },
  "model": {
    "model_version": "deterministic-v1",
    "route_scores": { "adyen": 0.91, "stripe": 0.64 }
  },
  "reason_codes": ["ADYEN_HEALTHY_FOR_SEGMENT", "HIGHER_EXPECTED_APPROVAL_RATE", "RISK_ACCEPTABLE"],
  "decision_latency_ms": 11.4,
  "ttl_ms": 3000
}

3. Handle the instruction#

Your backend switches on action and executes through your own payment stack. The seven possible instructions are route, use_default_route, step_up, hold, retry, failover, and block. See Decision instructions for what each one means.

ts
switch (decision.action) {
  case 'route':
  case 'failover':
    return chargeWithProvider(decision.route!.primary_provider, order);

  case 'use_default_route':
    return chargeWithDefaultProvider(order);

  case 'step_up':
    return requireThreeDS(order, decision.decision_id);

  case 'hold':
    return parkForReview(order, decision.decision_id);

  case 'retry':
    return retryWithPolicy(order, decision.route);

  case 'block':
    return rejectPayment(order, decision.decision_id, decision.reason_codes);
}

4. Report the outcome#

After your PSP call finishes, tell PayInference what happened. This feeds provider health, analytics, and policy evaluation. It is safe to fire and forget.

bash
curl -s http://localhost:4000/v1/outcomes \
  -H "content-type: application/json" \
  -H "x-api-key: pi_demo_key_123" \
  -d '{
    "decision_id": "dec_9f3a1c0b22d14e55",
    "order_id": "order_456",
    "provider_used": "adyen",
    "outcome": "approved",
    "provider_latency_ms": 842,
    "amount": 14900,
    "currency": "USD"
  }'

5. Use the SDK instead#

The Node.js SDK wraps the same two calls with validation, timeouts, retries, and a local fallback for when PayInference is unreachable:

ts
import { PayInferenceClient } from '@payinference/sdk-node';

const payinference = new PayInferenceClient({
  apiKey: process.env.PAYINFERENCE_API_KEY!,
  baseUrl: process.env.PAYINFERENCE_BASE_URL ?? 'http://localhost:4000',
  merchantId: 'm_123',
  timeoutMs: 50,
  fallback: { provider: 'stripe' },
});

const decision = await payinference.decide({
  order_id: 'order_456',
  transaction: { amount: 14900, currency: 'USD', country: 'US', payment_method: 'card' },
  available_providers: ['stripe', 'adyen', 'paypal'],
});

See the Node.js SDK guide for the full option set.

Next steps#