I've been building AI products for some time now, and every time I got close to putting something in a customer's hands I ran into the same questions.
How do my customers actually reach the models I want them to use, under names that belong to my product? How do I know which user spent what? How do I stop one of them from burning through way more than they pay me?
None of that is in the model. It is the glue around it, and I kept rewriting that glue.
You buy tokens. You sell plans.
You pay providers by the token. The invoice shows up at the end of the month as one number for everyone. Meanwhile you are selling a monthly plan, or seats, or credit packs. Those two things do not line up.
So most people start with a spreadsheet. Log the tokens, multiply by a price list you keep by hand, reconcile it later against the invoice. That works until someone leaves a loop running overnight and you find out from the provider email.
Looking at usage after the fact tells you what happened. It does not stop it. And checking the balance once at the start of a request is not enough either, because by the time tokens are streaming back, that number is already out of date. If you want a real spend limit, it has to hold while the response is still coming in.
What Seams does
Seams is that middle layer I kept having to build. Each product gets its own gateway host: we run it, your clients call it. Your users reach your models through that host, under names you choose. Routers and aliases decide how those names resolve; you change them without redeploying a client.
Mint one key per end user. Attribution lives on that key, not on a request header, so usage records what actually happened. Bundles decide what each key can reach. Every call is metered, and caps hold for the life of the stream.
Your users also get a usage page under your name, on your subdomain. They can see their spend, their keys, and their limit. You do not have to build that page. You just point at it.
If your users already have their own provider keys, they can bring them. They keep their own rate limits and their own bill with the provider. We still meter and cap spend on your side. We do not keep the key sitting around on disk.
We also do not store prompts or completions. There is nowhere in the usage record to put that, on purpose. And if we cannot measure a call, we fail it instead of guessing a number.
Try it in five minutes
This is the smallest path that produces a real number on a real usage page. You need a GitHub account and about five minutes.
1. Sign in and finish onboarding
Open the console at https://console.ourseams.com/sign-in, continue with GitHub, and walk the setup. You leave with an application (gateway + portal hostnames), a connected provider account, and a customer token (`sk_`).
Store the token somewhere your backend can read. Never put it in a browser or a mobile app.
export SEAMS_API_KEY="sk_acme_…" seams apps current # gateway https://<app>.gw.ourseams.com/v1 # portal https://<app>.portal.ourseams.com
2. Mint a key for one end user
One key per person. Attribution lives on the credential; there is no header to set. If that end user does not exist yet, mint creates them and opens their balance.
seams keys mint --user alice --bundle pro --cap 5.00 --label "Alice laptop" # ak_acme_… shown once; store it now
import { Seams } from "@ourseams/sdk" const seams = new Seams({ apiKey: process.env.SEAMS_API_KEY! }) const key = await seams.keys.mint({ endUserId: "alice", bundle: "pro", capMicros: "5000000", name: "Alice laptop", }) // key.key is shown once
3. Point the client at your gateway
Use your application hostname and the end user's `ak_`. OpenAI, Anthropic, and Gemini clients all work: same host, same key. The model name is yours (`acme/smart`), not the provider's.
import OpenAI from "openai" const client = new OpenAI({ baseURL: "https://<app>.gw.ourseams.com/v1", apiKey: endUserKey, }) await client.chat.completions.create({ model: "acme/smart", messages: [{ role: "user", content: "Say hello" }], })
import Anthropic from "@anthropic-ai/sdk" const client = new Anthropic({ baseURL: "https://<app>.gw.ourseams.com", apiKey: endUserKey, }) await client.messages.create({ model: "acme/smart", max_tokens: 1024, messages: [{ role: "user", content: "Say hello" }], })
import { GoogleGenAI } from "@google/genai" const client = new GoogleGenAI({ apiKey: endUserKey, httpOptions: { baseUrl: "https://<app>.gw.ourseams.com" }, }) await client.models.generateContent({ model: "acme/smart", contents: "Say hello", })
curl https://<app>.gw.ourseams.com/v1/chat/completions \ -H "Authorization: Bearer ak_acme_…" \ -H "Content-Type: application/json" \ -d '{ "model": "acme/smart", "messages": [{ "role": "user", "content": "Say hello" }] }'
4. Read the spend
Usage is queryable immediately. Hand alice a portal link when you want her to see the same numbers under your brand.
seams usage --user alice seams portal link alice # url https://<app>.portal.ourseams.com/enter?t=ps_…
Where we are
Sign-up is open. If you are selling an AI product and you are still reconciling spend in a spreadsheet, start at the console. The docs quickstart and the TypeScript / Python SDKs cover the same steps with more surface area.