Get started
Quickstart
Mint a key for one end user and make a real call through your gateway.
You need a customer token (sk_) from the console. Everything below is one end user, one key, one call, the smallest thing that produces a real number on a real page.
The whole thing, in commands
Every step below, with no language to choose. seams is the CLI (npm i -g @ourseams/cli); everything it does is also an HTTP call, and the same run in curl is on the second tab.
export SEAMS_API_KEY="sk_acme_…"
seams models add acme/smart --source anthropic/claude-sonnet-4
seams bundles add pro --name Pro
seams bundles allow pro acme/smart
# mint a key for one end user; the secret is in this response and nowhere else
seams keys mint --user alice --bundle pro --cap 50 --expires 1h
# your application's own hostnames, as full urls
seams apps show acme
# call the gateway with the end user's ak_
curl https://acme.gw.ourseams.com/v1/chat/completions \
-H "Authorization: Bearer $END_USER_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "acme/smart", "messages": [{ "role": "user", "content": "Say hello" }] }'
# what it cost
seams usage --user alicecurl -X POST https://api.ourseams.com/v1/keys \
-H "Authorization: Bearer $SEAMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"endUserId": "alice",
"email": "[email protected]",
"bundle": "pro",
"capMicros": "50000000",
"expiresAt": "2099-01-01T00:00:00Z"
}'
# { "id": "key_…", "key": "ak_acme_…", "keyPrefix": "ak_acme_a1b2c3",
# "endUserId": "alice", "capMicros": "50000000", "expiresAt": "2099-01-01T00:00:00Z" }Keep what mint gives you back: the id over the API, the keyPrefix the CLI prints. keys show takes either. An end-user id only works while that person holds exactly one key.
Step by step
- 1
Store your customer token
The
sk_token authenticates your backend to the Seams API. It mints end-user keys and reads usage. It is not the key your users hold.cliexport SEAMS_API_KEY="sk_acme_…"Never ship an
sk_to a browser or a mobile app. It can mint keys and read every customer's spend. - 2
Name a model and allow it on a bundle
Create your alias, then allow it on the bundle you will mint under. Skip
bundles addif onboarding already createdpro.seams models add acme/smart --source anthropic/claude-sonnet-4 seams bundles add pro --name Pro seams bundles allow pro acme/smartawait seams.models.add("acme/smart", { sources: [{ provider: "anthropic", model: "claude-sonnet-4" }], }) await seams.bundles.create("pro", { displayName: "Pro" }) await seams.bundles.allow("pro", { models: ["acme/smart"] })from seams import models seams.models.add( "acme/smart", sources=[models.AddModelSource(provider="anthropic", model="claude-sonnet-4")], ) seams.bundles.create("pro", display_name="Pro") seams.bundles.allow("pro", models=["acme/smart"])A router is optional. After its targets are on a bundle,
seams routers addattaches it. See Routers. - 3
Mint a key for one end user
Mint under your own identifier. If that person does not exist yet, mint creates them and opens their balance.
curl https://api.ourseams.com/v1/keys \ -H "Authorization: Bearer $SEAMS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "endUserId": "alice", "email": "[email protected]", "bundle": "pro", "capMicros": "50000000" }'import { Seams } from "@ourseams/sdk" const seams = new Seams({ apiKey: process.env.SEAMS_API_KEY! }) const key = await seams.keys.mint({ endUserId: "alice", email: "[email protected]", bundle: "pro", capMicros: "50000000", }) // secret is shown once console.log(key.id, key.key)import os from seams import Seams seams = Seams(api_key=os.environ["SEAMS_API_KEY"]) key = seams.keys.mint( end_user_id="alice", email="[email protected]", bundle="pro", cap_micros="50000000", ) # secret is shown once print(key.id, key.key)The secret is never retrievable again. Save it before you do anything else.
- 4
Point the client at your gateway
Your application has its own gateway host. It speaks the OpenAI, Anthropic, and Gemini wire formats, so the client you already use works unchanged. Point that host as the base URL, and pass the end user's
ak_instead of your provider key.import OpenAI from "openai" const client = new OpenAI({ baseURL: "https://<app>.gw.ourseams.com/v1", apiKey: endUserKey, }) const res = 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, }) const res = 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" }, }) const res = await client.models.generateContent({ model: "acme/smart", contents: "Say hello", })# openai.chat curl https://<app>.gw.ourseams.com/v1/chat/completions \ -H "Authorization: Bearer $END_USER_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "acme/smart", "messages": [{ "role": "user", "content": "Say hello" }] }' # anthropic.messages curl https://<app>.gw.ourseams.com/v1/messages \ -H "Authorization: Bearer $END_USER_KEY" \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "acme/smart", "max_tokens": 1024, "messages": [{ "role": "user", "content": "Say hello" }] }' # google.generate curl "https://<app>.gw.ourseams.com/v1beta/models/acme%2Fsmart:generateContent" \ -H "Authorization: Bearer $END_USER_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [{ "text": "Say hello" }] }] }' - 5
Read what it cost
Usage is queryable immediately, and the same number is on the customer's portal page.
curl "https://api.ourseams.com/v1/usage?endUserId=alice" \ -H "Authorization: Bearer $SEAMS_API_KEY"const { totals } = await seams.usage({ endUserId: "alice" }) console.log(totals.calls, totals.billedMicros)usage = seams.usage(end_user_id="alice") print(usage.totals.calls, usage.totals.billed_micros)
What you just built
- An alias on a bundle, and a key that may call it
- A metered call with a usage row and portal spend under your brand