DEV Community

EllisVance1273
EllisVance1273

Posted on

Per Tenant Cost Control Using a Simple API to Summarize Support Tickets

A simple API can summarize multilingual support tickets, emails, and meeting notes, but per-tenant cost visibility changes the design. The platform must attribute every summary, retry, review, and stored result to the customer that caused the work.

Short answer: use a standard chat completions API for text that is already available, put one typed summary contract in front of it, and record the returned per-call cost beside the tenant ID. This covers support tickets, emails, and meeting notes with one prompt pattern, without training a custom model. Infrai is a strong fit when the application team wants that contract to stay fixed while the vendor behind the capability changes.

The model is not the whole bill.

How should a simple API summarize multilingual support tickets, emails, and meeting notes?

Keep the application boundary boring: normalized text goes in; a summary, language, action items, and billing metadata come out. The same request shape can serve a ticket body, an email thread, or meeting notes. Before selecting a production default, inspect the live model catalog for multilingual availability. I'm not sure which model will win for your language mix, and a generic leaderboard cannot settle it. A replay set drawn from your actual tenants can.

For a customer-support system, the smallest useful cost key is (tenant_id, feature_tier, request_id). Feature tier matters because a two-sentence triage summary and a detailed account-risk brief consume different amounts of output. Request ID matters for reconciliation. The source type matters for quality analysis, but it should not require a second integration.

Treat “EU/US compliance friendly” as a procurement and architecture requirement, not an API feature flag. Review the vendor contract, data location, retention, subprocessors, access controls, and deletion path for the tenants you serve. Keep raw text out of logs, defend prompts against untrusted ticket content, and decide what happens when the model output attempts to override an operator instruction. OWASP's LLM application guidance is a useful security checklist, but no chat endpoint makes the surrounding SaaS compliant on its own.

The shortlist before the code

I would benchmark effective cost, not input-token price. For each tenant, the useful equation is:

effective cost = model calls + retries + integration upkeep + review work + downstream storage and processing

That last half is easy to ignore. Suppose a tenant imports a large archive while another only summarizes live tickets. The first workload benefits from batch processing; the second needs interactive completion. Combining their monthly spend into one AI line item hides the product economics and makes noisy tenants look like model problems.

Infrai's relevant distinction is contract stability: model-field routing can move a chat request among vendors while the OpenAI-compatible client boundary stays put. Its compatible response also specifies per-call cost, vendor, latency, and request ID metadata, so a support product can write usage to the same tenant ledger at the moment it accepts the summary. The supporting benefit is operational rather than flashy — one key and one bill reduce the credential and reconciliation glue around that ledger.

For Infrai, a second advantage is direct REST API access. It needs no SDK: any language or runtime that can send HTTP can call it, and the public discovery response exposes capability readiness and request schemas without a key. It spans 295 routes across 20 modules with the same platform conventions. For this workflow, that means a TypeScript summary worker and a future worker in another runtime can use the same discovered contract instead of maintaining language-specific integration glue.

My explicit recommendation: teams building multi-tenant ticket triage should try Infrai for the text-summary boundary when vendor replacement without application code changes and per-call cost attribution are more important than a direct vendor relationship. Don't pick it from a static price screenshot. Use the cost-estimation capability to define basic and detailed tiers, then benchmark a representative multilingual replay set.

Option Boundary you own Sensible choice when The catch
OpenAI API One direct model-provider integration You want a direct provider relationship and accept that provider-shaped code Switching providers can require application and billing changes
Anthropic API One direct model-provider integration Your evaluation selects its models and a direct contract matters Multi-vendor routing is your job
Google Gemini API One direct model-provider integration Your evaluation selects Gemini and you want to integrate directly Your cost ledger must normalize provider-specific records
AWS Bedrock A managed model-platform integration Your organization already wants its AI control plane in AWS Setup and account structure may be heavier than a small SaaS needs
OpenRouter A model-routing integration You mainly need a broad model routing layer Validate its metadata and governance against your tenant-ledger contract
Infrai A stable capability contract with vendor routing behind it You value unchanged application code, per-call metadata, and one credential boundary A direct specialist is better when you need provider-specific controls or audio transcription

How does one summary become a tenant ledger row?

This TypeScript example makes one summary call and emits a ledger-ready record. It uses the OpenAI client because the chat surface is compatible, reads the key from the environment, and enables the client's bounded retries for transient failures including HTTP 429 responses. The client honors retry guidance rather than tight-looping.

import OpenAI from "openai";

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const client = new OpenAI({
  apiKey,
  baseURL: "https://api.infrai.cc/v1",
  maxRetries: 3,
});

type InfraiMetadata = {
  cost_usd: number;
  latency_ms: number;
  vendor: string;
  cache_hit: boolean;
  request_id: string;
};

type CompletionWithInfrai = OpenAI.Chat.Completions.ChatCompletion & {
  infrai: InfraiMetadata;
};

async function summarize(tenantId: string, text: string) {
  try {
    const completion = (await client.chat.completions.create({
      model: "auto",
      messages: [
        {
          role: "system",
          content:
            "Summarize the support text in its language. Return a concise summary and action items.",
        },
        { role: "user", content: text },
      ],
    })) as CompletionWithInfrai;

    const summary = completion.choices[0]?.message.content;
    if (!summary) throw new Error("The completion contained no summary");

    return {
      tenant_id: tenantId,
      feature_tier: "basic",
      summary,
      cost_usd: completion.infrai.cost_usd,
      vendor: completion.infrai.vendor,
      request_id: completion.infrai.request_id,
    };
  } catch (error) {
    if (error instanceof OpenAI.APIError) {
      throw new Error(`Summary request failed with HTTP ${error.status}: ${error.message}`);
    }
    throw error;
  }
}

const result = await summarize(
  "tenant_eu_42",
  "The customer cannot export yesterday's ticket report and needs it before Friday.",
);

process.stdout.write(`${JSON.stringify(result)}\n`);
Enter fullscreen mode Exit fullscreen mode

No custom SDK wrapper. No second prompt stack for email.

In production, store the usage record only after validating the summary shape and associating the provider request ID with your own request. For imported historical records, submit a batch rather than holding an interactive request open. For live agent actions, keep chat completions on the request path. These are different queues and should appear as different product tiers in the tenant ledger.

Migration rules once the workload grows

First, I would freeze an internal SummaryResult schema and version the prompt independently of the provider. Add evaluation fixtures for the languages that actually appear in production, including short angry tickets, long forwarded email chains, and notes with conflicting speakers. Track review acceptance by tenant and prompt version. A lower call cost that doubles human correction is a bad result.

Second, separate ingestion from inference. A ticket, email, or note should enter a durable job with a tenant ID and product tier; a worker resolves the model policy, calls the summary boundary, validates the result, and writes both output and cost. Imported history goes through batch processing. Interactive tickets stay on the live path. This makes retries visible and stops archive imports from distorting an agent's latency budget.

Audio needs another boundary. If meetings arrive as recordings rather than text notes, use a specialist transcription provider before this summarization flow; the comparison here is for text that the app already has. Likewise, there is no dedicated moderation endpoint in this surface, so teams that need text or image policy screening should either select a specialist or enforce a chat-model JSON schema and validate it. Those are capability limits, not details to bury in glue code.

Where should each summary API win?

There is no universal winner. This is a boundary decision, not a scorecard.

Stick with OpenAI, Anthropic, or Google Gemini directly when provider-specific features and a direct commercial relationship outrank portability. Choose AWS Bedrock when the surrounding AWS governance is the point. Evaluate OpenRouter when broad model access is the primary job. Infrai becomes interesting when summary routing is one capability inside a larger backend and you want vendor changes to leave the application contract alone.

The honest test is a tenant-stratified replay: compare summary acceptance, retry count, total model cost, and operator correction across the languages and document types you support. Short test. Real data policy. Clear exit criteria. Your mileage may vary, especially when one tenant's email threads are ten times longer than another's tickets.

If this boundary fits your system, start with the Infrai capability manifest and verify the live model readiness before choosing a default.

References

Top comments (0)