§ SDK · Node.js

Integrate with @nuvouch/node

Use the official SDK when you want typed payloads, predictable error objects, and webhook utilities without manually wiring request signing or parsing.

install
npm
npm install @nuvouch/node
bootstrap.ts
from env
import { Nuvouch } from "@nuvouch/node";

const nuvouch = new Nuvouch({
  apiKey: process.env.NUVOUCH_API_KEY,
});
// reads NUVOUCH_API_KEY
§ SDK · Connections

Link business subjects once, then reuse them

New integrations should link the subject once, then keep the subject and source mapping stable in your own product. Use the same approved recipient and origin for future requests instead of inventing a new identity each time.

create-connection-session.ts
sdk
import { Nuvouch } from "@nuvouch/node";

const nuvouch = new Nuvouch({ apiKey: process.env.NUVOUCH_API_KEY });

const link = await nuvouch.links.create({
  subject: {
    id: "cus_123",
    label: "Ada Lovelace",
  },
  source: {
    key: "merchant:acct_live_001",
    name: "Stripe Live Account",
  },
});

console.log(link.id, link.url, link.shortCode);
§ SDK · Connection lifecycle

Inspect and revoke existing connections

Fetch session state

get-connection-session.ts
const link = await nuvouch.links.retrieve("conn_sess_123");
console.log(link.status);
console.log(link.connection?.id);

Lookup the active link for a subject tuple

lookup-connection.ts
const result = await nuvouch.connections.lookup({
  subjectId: "cus_123",
  sourceKey: "merchant:acct_live_001",
});

console.log(result?.nuvouchUserId);

List connections with filters

list-connections.ts
const result = await nuvouch.connections.list({
  subjectId: "cus_123",
  status: "active",
});

console.log(result.data.map((item) => item.id));

Revoke a link

revoke-connection.ts
const result = await nuvouch.connections.revoke("conn_123");
console.log(result.status); // revoked
§ SDK · Approvals

Create a payment authorization request

POST/v1/approvals
create-approval.ts
SDK
const approval = await nuvouch.approvals.create({
  subject: {
    id: "user_123"
  },
  source: {
    key: "stripe:acct_123",
    name: "Stripe"
  },
  action: {
    type: "payment.authorization",
    title: "Approve payment",
    description: "Allow Billing Agent to charge $84.00"
  },
  amount: {
    value: 84.0,
    currency: "USD"
  },
  details: [
    { label: "Merchant", value: "OpenAI API" },
    { label: "Billing period", value: "April 2026" }
  ],
  actor: {
    type: "ai_agent",
    name: "Billing Agent"
  },
  risk: {
    level: "medium",
    signals: ["automated_action", "usage_threshold_exceeded"]
  },
  decisions: [
    { label: "Approve", value: "approve" },
    { label: "Deny", value: "deny" }
  ],
  metadata: {
    orderId: "ord_123"
  }
});
§ SDK · Request lifecycle

Read or cancel request state

Fetch by request id

get.ts
const current = await nuvouch.approvals.retrieve("req_2f5cd6dbb6784b9ab2f7");
console.log(current.status); // pending | approved | denied | expired | cancelled

Fetch by your external id

get-by-external.ts
const current = await nuvouch.approvals.retrieveByExternalId("payment_auth_001");

Cancel a pending request

cancel.ts
const cancelled = await nuvouch.approvals.cancel("req_2f5cd6dbb6784b9ab2f7");
§ SDK · Agent Auth

Use the dedicated Agent Auth references

The Node SDK includes Agent Auth runtime and provider helpers, but the full direct runtime flow now lives in the AI Agent Auth reference. Keep this SDK page focused on approvals, links, webhooks, and general client setup.

FieldTypeDescription
/docs/agent-authdirect runtimeRuntime-generated QR pairing, delegation request/wait/token, provider introspection, final approval, and CLI usage.
/docs/mcphosted MCPOfficial OAuth-protected MCP discovery, registration, authorization, token, pairing, and MCP delegation tools.
§ SDK · Reliability

Error and retry behavior

FieldTypeDescription
NuvouchApiErrorerror classThrown for all non-2xx responses.
statusnumberHTTP status from the API response.
codestringStable machine-readable Nuvouch error code when provided.
retryAfterSecondsnumberParsed from Retry-After for rate-limited requests.
error-handling.ts
import {
  NuvouchApiError,
  isRateLimitError,
  isRetryableError,
} from "@nuvouch/node";

const input = {
  subject: {
    id: "user_123",
  },
  source: {
    key: "stripe:acct_123",
  },
  action: {
    type: "payment.authorization",
    title: "Approve payment",
    description: "Allow Billing Agent to charge $84.00",
  },
  amount: {
    value: 84.0,
    currency: "USD",
  },
  actor: {
    type: "ai_agent",
    name: "Billing Agent",
  },
  risk: {
    level: "medium",
    signals: ["automated_action", "usage_threshold_exceeded"],
  },
  decisions: [
    { label: "Approve", value: "approve" },
    { label: "Deny", value: "deny" }
  ],
  externalRequestId: "payment_auth_001"
};

try {
  await nuvouch.approvals.create(input, {
    idempotencyKey: "worker_retry_payment_auth_001",
  });
} catch (error) {
  if (isRateLimitError(error)) {
    return;
  }
  if (
    error instanceof NuvouchApiError &&
    error.code === "DUPLICATE_EXTERNAL_ID"
  ) {
    return nuvouch.approvals.retrieveByExternalId("payment_auth_001");
  }
  if (error instanceof NuvouchApiError) {
    console.error(error.code, error.status, error.message);
  }
  if (!isRetryableError(error)) return;
}
§ SDK · Webhooks

Verify signed callbacks

callback-handler.ts
import { Nuvouch } from "@nuvouch/node";

const nuvouch = new Nuvouch({ apiKey: process.env.NUVOUCH_API_KEY });
const seenDeliveries = new Set<string>();

export async function handleNuvouchWebhook(req: Request) {
  const rawBody = await req.text();
  const event = nuvouch.webhooks.verify({
    rawBody,
    signature: req.headers.get("x-nuvouch-signature"),
    secret: process.env.NUVOUCH_CALLBACK_SECRET!,
  });

  const deliveryId = req.headers.get("x-nuvouch-delivery");
  if (deliveryId && seenDeliveries.has(deliveryId)) {
    return new Response("duplicate", { status: 200 });
  }
  if (deliveryId) seenDeliveries.add(deliveryId);

  if (nuvouch.webhooks.isApprovalEvent(event) && event.type === "nuvouch.approval_request.approved") {
    // commit payment capture side effects exactly once
  }
  if (nuvouch.webhooks.isConnectionEvent(event) && event.type === "nuvouch.connection.accepted") {
    // mark subject tuple as linked in your system
  }
  return new Response("ok");
}

Use a secret manager instead of env vars

callback-handler-secret-manager.ts
import { Nuvouch } from "@nuvouch/node";

const nuvouch = new Nuvouch({ apiKey: process.env.NUVOUCH_API_KEY });

export async function handleNuvouchWebhook(req: Request) {
  const rawBody = await req.text();
  const callbackSecret = await secrets.get("integrations/nuvouch/callback-secret");

  const event = nuvouch.webhooks.verify({
    secret: callbackSecret,
    rawBody,
    signature: req.headers.get("x-nuvouch-signature"),
  });

  console.log("Verified event:", event.type, event.deliveryId);
  return new Response("ok");
}

Continue with the raw webhook contract on webhook reference.