Summarize Moderation Text Using Claude API in Node.js with Per-Tenant Review Budgets A developer outlined a Node.js pattern for summarizing moderation reports via an OpenAI-compatible chat-completions endpoint, enforcing per-tenant token budgets before each request and falling back to a 'review-needed' state when summaries are empty, ambiguous, or truncated. The approach keeps provider details at the boundary, uses idempotency keys per tenant and report, and retries rate-limited calls with exponential backoff. The summary is treated as an aid to human review rather than a moderation decision. To summarize moderation text using the Claude API in Node.js, set a per-tenant token budget before sending a report to a summarizer. That is the decision rule I would ship: use Claude for concise first-pass summaries when the provider boundary can expose usage, then keep the original report and route ambiguous output to a human. The budget belongs beside the tenant ID, not in a global dashboard. This is a B2B SaaS workflow. A tenant submits a report, the service asks a model for a short review brief, and a human reviewer sees the brief with the source text. The useful engineering question is less "which model wins?" than "can I explain this tenant's usage and recover when the summary is incomplete?" The summary is not the decision. The data flow is small: validate the report, estimate its input size, check the tenant budget, request a concise summary, and store the result with the original report. A summary is an aid to review, not a moderation decision. If the response is empty, ambiguous, or cut off, the safe result is a review-needed state. The example below keeps the provider details at the boundary. It uses Infrai's OpenAI-compatible chat route, with a verified model ID supplied by default, so the same Node.js code can sit behind a Claude-oriented adapter without learning another SDK. Set INFRAI BASE URL to the documented API base and override INFRAI MODEL only after checking the live model list. The request uses the chat-completions shape; axios handles transport, while the application owns budget and fallback policy. python import axios, { AxiosError } from "axios"; const baseUrl = process.env.INFRAI BASE URL; const apiKey = process.env.INFRAI API KEY; const model = process.env.INFRAI MODEL ?? "deepseek-v4-flash-0731"; if baseUrl || apiKey { throw new Error "INFRAI BASE URL and INFRAI API KEY are required" ; } const endpoint = ${baseUrl}/chat/completions ; type SummaryResult = { status: "ready" | "review-needed"; summary: string | null; reason?: string; }; function estimateTokens text: string : number { return Math.ceil text.length / 4 ; } function retryDelay error: AxiosError, attempt: number : number { const retryAfter = error.response?.headers "retry-after" ; const seconds = Number retryAfter ; if Number.isFinite seconds && seconds = 0 return seconds 1000; return Math.min 1000 2 attempt, 8000 ; } function sleep ms: number : Promise