---
title: "Integration Examples"
description: "Node.js, Python, cURL, and webhook handler examples."
collection: "guides"
slug: "integration-examples"
url: "https://naiza.ai/docs/guides/integration-examples"
markdown: "https://naiza.ai/docs/guides/integration-examples.md"
full_docs: "https://naiza.ai/docs.md"
product: "Naiza"
base_url: "https://api.naiza.ai/api/v1"
---

# Integration Examples

> Node.js, Python, cURL, and webhook handler examples.

## Table of contents

- [Node.js Examples](#nodejs-examples)
  - [Basic Client Class](#basic-client-class)
  - [Idempotency Pattern](#idempotency-pattern)
  - [Retry with Exponential Backoff](#retry-with-exponential-backoff)
  - [Correlation ID Tracking](#correlation-id-tracking)
- [Python Examples](#python-examples)
  - [Basic Client Class](#basic-client-class)
  - [Idempotency Pattern](#idempotency-pattern)
  - [Retry with Exponential Backoff](#retry-with-exponential-backoff)
- [cURL Examples](#curl-examples)
  - [Evaluate Decision](#evaluate-decision)
  - [Query Decisions with Filters](#query-decisions-with-filters)
  - [Submit Feedback with Idempotency](#submit-feedback-with-idempotency)
- [Webhook Handler Examples](#webhook-handler-examples)
  - [Express.js Webhook Handler](#expressjs-webhook-handler)
  - [Flask Webhook Handler](#flask-webhook-handler)
- [Best Practices](#best-practices)
  - [1. Environment Variables](#1-environment-variables)
  - [2. Error Handling](#2-error-handling)
  - [3. Request Timeouts](#3-request-timeouts)
  - [4. Logging](#4-logging)

Code examples for common integration patterns.

## Node.js Examples

### Basic Client Class

```javascript
class NaizaClient {
  constructor(apiKey, baseUrl = "https://api.naiza.ai/api/v1") {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async request(method, path, body = null) {
    const options = {
      method,
      headers: {
        "x-api-key": this.apiKey,
        "Content-Type": "application/json",
      },
    };

    if (body) {
      options.body = JSON.stringify(body);
    }

    const response = await fetch(`${this.baseUrl}${path}`, options);

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error?.message || `HTTP ${response.status}`);
    }

    return response.json();
  }

  async evaluateDecision(data) {
    return this.request("POST", "/decisions/evaluate", data);
  }

  async getDecision(id) {
    return this.request("GET", `/decisions/${id}`);
  }

  async listDecisions(query = {}) {
    const params = new URLSearchParams(query);
    return this.request("GET", `/decisions?${params}`);
  }

  async ingestEvent(data) {
    return this.request("POST", "/events", data);
  }

  async ingestEventAsync(data) {
    return this.request("POST", "/events/async", data);
  }

  async mintWebSdkToken(data) {
    return this.request("POST", "/websdk/tokens", data);
  }

  async blockIp(ip, reason) {
    return this.request("POST", "/lists/ip", { ip, reason });
  }

  async createWebhookSubscription(url, eventTypes) {
    return this.request("POST", "/webhooks/subscriptions", { url, eventTypes });
  }
}
```

### Idempotency Pattern

```javascript
async function submitFeedbackWithIdempotency(decisionId, label, notes) {
  // Generate stable idempotency key
  const idempotencyKey = `feedback-${decisionId}-${label}`;

  try {
    const response = await fetch(
      `https://api.naiza.ai/api/v1/feedback/decision/${decisionId}`,
      {
        method: "POST",
        headers: {
          "x-api-key": process.env.NAIZA_API_KEY,
          "Idempotency-Key": idempotencyKey,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ label, notes }),
      },
    );

    if (!response.ok) {
      const error = await response.json().catch(() => ({}));
      throw new Error(error?.error?.message || `HTTP ${response.status}`);
    }

    return response.json();
  } catch (error) {
    // Retry with same idempotency key
    console.error("Error submitting feedback:", error);
    throw error;
  }
}
```

### Retry with Exponential Backoff

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (response.ok) {
        return response;
      }

      // Don't retry client errors (4xx)
      if (response.status >= 400 && response.status < 500) {
        throw new Error(`Client error: ${response.status}`);
      }

      // Retry server errors (5xx) and rate limits (429)
      if (response.status >= 500 || response.status === 429) {
        const retryAfter =
          response.headers.get("Retry-After") || Math.pow(2, attempt);
        await sleep(retryAfter * 1000);
        continue;
      }

      throw new Error(`HTTP ${response.status}`);
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      await sleep(Math.pow(2, attempt) * 1000);
    }
  }
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
```

### Correlation ID Tracking

```javascript
class TransactionTracker {
  constructor() {
    this.transactions = new Map();
  }

  async evaluateTransaction(transactionData) {
    const response = await fetch(
      "https://api.naiza.ai/api/v1/decisions/evaluate",
      {
        method: "POST",
        headers: {
          "x-api-key": process.env.NAIZA_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(transactionData),
      },
    );

    const decision = await response.json();
    const { requestId, eventId } = decision.correlationIds;

    // Store for later reference
    this.transactions.set(requestId, {
      eventId,
      decision: decision.decision,
      riskScore: decision.riskScore,
      timestamp: new Date(),
    });

    return decision;
  }

  getTransaction(requestId) {
    return this.transactions.get(requestId);
  }

  async getDecisionDetails(requestId) {
    const transaction = this.transactions.get(requestId);
    if (!transaction) return null;

    const response = await fetch(
      `https://api.naiza.ai/api/v1/decisions/${transaction.eventId}`,
      {
        headers: { "x-api-key": process.env.NAIZA_API_KEY },
      },
    );

    return response.json();
  }
}
```

## Python Examples

### Basic Client Class

```python
import requests
import os
from typing import Optional, Dict, Any, List

class NaizaClient:
    def __init__(self, api_key: str, base_url: str = 'https://api.naiza.ai/api/v1'):
        self.api_key = api_key
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'x-api-key': api_key,
            'Content-Type': 'application/json',
        })

    def _request(self, method: str, path: str, **kwargs) -> Dict[str, Any]:
        url = f'{self.base_url}{path}'
        response = self.session.request(method, url, **kwargs)
        response.raise_for_status()
        return response.json()

    def evaluate_decision(self, data: Dict[str, Any]) -> Dict[str, Any]:
        return self._request('POST', '/decisions/evaluate', json=data)

    def get_decision(self, decision_id: str) -> Dict[str, Any]:
        return self._request('GET', f'/decisions/{decision_id}')

    def list_decisions(self, **query_params) -> Dict[str, Any]:
        return self._request('GET', '/decisions', params=query_params)

    def ingest_event(self, data: Dict[str, Any]) -> Dict[str, Any]:
        return self._request('POST', '/events', json=data)

    def ingest_event_async(self, data: Dict[str, Any]) -> Dict[str, Any]:
        return self._request('POST', '/events/async', json=data)

    def mint_websdk_token(self, data: Dict[str, Any]) -> Dict[str, Any]:
        return self._request('POST', '/websdk/tokens', json=data)

    def block_ip(self, ip: str, reason: str) -> Dict[str, Any]:
        return self._request('POST', '/lists/ip', json={'ip': ip, 'reason': reason})

    def create_webhook_subscription(self, url: str, event_types: List[str]) -> Dict[str, Any]:
        return self._request('POST', '/webhooks/subscriptions', json={'url': url, 'eventTypes': event_types})
```

### Idempotency Pattern

```python
def submit_feedback_with_idempotency(
    client: NaizaClient,
    decision_id: str,
    label: str,
    notes: str
) -> Optional[Dict[str, Any]]:
    # Generate stable idempotency key
    idempotency_key = f'feedback-{decision_id}-{label}'

    try:
        url = f'{client.base_url}/feedback/decision/{decision_id}'
        response = client.session.post(
            url,
            headers={'Idempotency-Key': idempotency_key},
            json={'label': label, 'notes': notes},
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError:
        raise
```

### Retry with Exponential Backoff

```python
import time
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session_with_retry():
    session = requests.Session()

    retry_strategy = Retry(
        total=3,
        backoff_factor=1,
        status_forcelist=[429, 500, 502, 503, 504],
        allowed_methods=["GET", "POST", "DELETE"]
    )

    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)

    return session
```

## cURL Examples

### Evaluate Decision

```bash
#!/bin/bash

API_KEY="naiza_api_sk_live_YOUR_KEY_HERE"
BASE_URL="https://api.naiza.ai/api/v1"

# Evaluate transaction
curl -X POST "${BASE_URL}/decisions/evaluate" \
  -H "x-api-key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "eventName": "payment.attempt",
    "customer": {
      "externalId": "cust_123",
      "email": "user@example.com"
    },
    "ip": "203.0.113.42"
  }'
```

### Query Decisions with Filters

```bash
#!/bin/bash

API_KEY="naiza_api_sk_live_YOUR_KEY_HERE"
BASE_URL="https://api.naiza.ai/api/v1"

# Query blocked decisions from last 24 hours (Unix seconds)
FROM_TS=$(($(date -u +%s) - 86400))
TO_TS=$(date -u +%s)

curl -X GET "${BASE_URL}/decisions?decision=deny&from=${FROM_TS}&to=${TO_TS}&limit=50" \
  -H "x-api-key: ${API_KEY}"
```

### Submit Feedback with Idempotency

```bash
#!/bin/bash

API_KEY="naiza_api_sk_live_YOUR_KEY_HERE"
BASE_URL="https://api.naiza.ai/api/v1"
DECISION_ID="evt_ckm9876543210"
IDEMPOTENCY_KEY="feedback-${DECISION_ID}-$(date +%s)"

curl -X POST "${BASE_URL}/feedback/decision/${DECISION_ID}" \
  -H "x-api-key: ${API_KEY}" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "FRAUD",
    "notes": "Customer confirmed fraud",
    "externalCaseId": "case_12345"
  }'
```

## Webhook Handler Examples

### Express.js Webhook Handler

```javascript
const express = require("express");
const crypto = require("crypto");

const app = express();

// Middleware to verify webhook signature
function verifyWebhookSignature(req, res, next) {
  const signature = req.headers["x-webhook-signature"];
  const secret = process.env.WEBHOOK_SIGNING_SECRET;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(req.body)
    .digest("hex");

  const provided = signature.replace("sha256=", "");

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided))) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  next();
}

// Webhook endpoint
app.post(
  "/webhooks/decisions",
  express.raw({ type: "application/json" }),
  verifyWebhookSignature,
  async (req, res) => {
    const payload = JSON.parse(req.body.toString("utf8"));
    const { eventType, data } = payload;

    // Process asynchronously
    processWebhook(eventType, data).catch(console.error);

    // Respond quickly
    res.status(200).json({ received: true });
  },
);

async function processWebhook(eventType, data) {
  switch (eventType) {
    case "EVENT_RISK_EVALUATED":
      await handleRiskEvaluated(data);
      break;
    case "EVENT_FEEDBACK_SUBMITTED":
      await handleFeedbackSubmitted(data);
      break;
    case "BLOCKLIST_ENTRY_ADDED":
      await handleBlocklistAdded(data);
      break;
    case "BLOCKLIST_ENTRY_REMOVED":
      await handleBlocklistRemoved(data);
      break;
  }
}

async function handleRiskEvaluated(decision) {
  console.log("New decision:", decision.decision);
  console.log("Risk score:", decision.riskScore);

  // Update your system based on decision
  if (decision.decision === "deny") {
    await blockTransaction(decision.correlationIds.eventId);
  }
}

async function handleFeedbackSubmitted(data) {
  console.log("Feedback submitted:", data);
}

async function handleBlocklistAdded(data) {
  console.log("Blocklist entry added:", data);
}

async function handleBlocklistRemoved(data) {
  console.log("Blocklist entry removed:", data);
}
```

### Flask Webhook Handler

```python
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
import os

app = Flask(__name__)

def verify_webhook_signature(raw_body, signature, secret):
    expected = hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()

    provided = signature.replace('sha256=', '')
    return hmac.compare_digest(expected, provided)

@app.route('/webhooks/decisions', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('X-Webhook-Signature')
    secret = os.getenv('WEBHOOK_SIGNING_SECRET')

    raw_body = request.get_data()
    if not verify_webhook_signature(raw_body, signature, secret):
        return jsonify({'error': 'Invalid signature'}), 401

    payload = json.loads(raw_body.decode('utf-8'))
    event_type = payload.get('eventType')
    data = payload.get('data')

    # Process webhook
    process_webhook(event_type, data)

    return jsonify({'received': True}), 200

def process_webhook(event_type, data):
    if event_type == 'EVENT_RISK_EVALUATED':
        handle_risk_evaluated(data)
    elif event_type == 'EVENT_FEEDBACK_SUBMITTED':
        handle_feedback_submitted(data)
    # ... handle other event types

def handle_risk_evaluated(decision):
    print(f"New decision: {decision['decision']}")
    print(f"Risk score: {decision['riskScore']}")

    if decision['decision'] == 'deny':
        block_transaction(decision['correlationIds']['eventId'])

def handle_feedback_submitted(data):
    print(f"Feedback submitted: {data}")
```

## Best Practices

### 1. Environment Variables

Always store API keys in environment variables:

```bash
# .env
NAIZA_API_KEY=naiza_api_sk_live_...
WEBHOOK_SIGNING_SECRET=whsec_...
```

```javascript
// Node.js
const apiKey = process.env.NAIZA_API_KEY;
```

```python
# Python
import os
api_key = os.getenv('NAIZA_API_KEY')
```

### 2. Error Handling

Always handle errors gracefully:

```javascript
try {
  const decision = await evaluateTransaction(data);
  // Process decision
} catch (error) {
  if (error.message.includes("Quota exceeded")) {
    // Handle quota exhaustion
  } else if (error.message.includes("Invalid API key")) {
    // Handle authentication error
  } else {
    // Log and handle other errors
    console.error("API error:", error);
  }
}
```

### 3. Request Timeouts

Set appropriate timeouts:

```javascript
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 seconds

const response = await fetch(url, {
  ...options,
  signal: controller.signal,
});

clearTimeout(timeoutId);
```

### 4. Logging

Log all API interactions for debugging:

```javascript
function logApiCall(method, path, status, duration) {
  console.log({
    method,
    path,
    status,
    duration,
    timestamp: new Date().toISOString(),
  });
}
```

## Related documentation

- [Overview](https://naiza.ai/docs/guides/overview.md) — Base URL, authentication, decision types, risk scores, and rate limits.
- [Getting Started](https://naiza.ai/docs/guides/getting-started.md) — Make your first Naiza API call and verify your integration.
- [Tenant Onboarding](https://naiza.ai/docs/guides/tenant-onboarding.md) — Create tenants, invite operators, and configure webhooks.
- [Web SDK — Install & CDN](https://naiza.ai/docs/guides/web-sdk-install.md) — Install the browser SDK via CDN, self-host, and verify ingest.
- [Quick Start](https://naiza.ai/docs/guides/quick-start.md) — Common operations for events, decisions, lists, and feedback.
- [Event Monitoring](https://naiza.ai/docs/guides/event-monitoring.md) — Model product events and turn rule outcomes into decisions.
- [AML Integration](https://naiza.ai/docs/guides/aml-integration.md) — Screen customers and counterparties against AML watchlists.
- [Best Practices](https://naiza.ai/docs/guides/best-practices.md) — Production guidance for keys, idempotency, and enforcement.

---

*Source: [https://naiza.ai/docs/guides/integration-examples](https://naiza.ai/docs/guides/integration-examples) · Full docs: [https://naiza.ai/docs.md](https://naiza.ai/docs.md)*
