Ceilings
Every key stops where you tell it to.
Give a key a token limit and a request limit when you create it. Usage counts up against both, so one runaway client can't drain the balance the rest of your users are sharing.
api.bitmesh.ai
Your app signs one request to one endpoint. We hold the provider credentials, count the request against the ceiling you set for that key, and forward it to the model you asked for.
Client · what leaves the device
HTTPS hides it from strangers on the network, but not from the person holding the phone. Anyone can run your app behind a debugging proxy and read what it sends. Here is that request, captured both ways.
POST /v1/chat/completions
Host: api.openai.com
Authorization: Bearer sk-proj-7Qf2VnR8kLmT0eXwZaB9x
# the same header, from someone else's machine $ curl api.openai.com/v1/chat/completions -H "Authorization: Bearer sk-proj-7Qf2…" 200 OK
The header is the key. Copy it once and it works from anywhere, against your whole provider account, until you rotate it and get a new build through review.
POST /chat Host: api.bitmesh.ai Authorization: OAuth oauth_consumer_key="a71f4c9e83b2d5", oauth_nonce="f3b0c1d87a2e4496", oauth_timestamp="1760342400", oauth_signature="k9QmT2x8Vd0rLw%3D" X-Payload-Signature: 9c1e47a0b6d2f385
# the same request, a few minutes later $ curl api.bitmesh.ai/chat -H "Authorization: OAuth oauth_consumer_key=…" 401 {"error":"Request timestamp is too old"}
The secret never goes over the network. What travels is a signature the gate stops accepting shortly after it was made, so a captured request goes stale instead of becoming a permanent key.
Then they can find the consumer secret, as they could find any credential in a build. Keeping it in the Keychain or Keystore makes that harder, not impossible.
What changes is how much it is worth. It is a key you issued, not your provider account: it stops at the token and request ceiling you set, can be pinned to one model, and is dead the moment you delete it. Your provider key was never in the build to find.
Handshake · POST /chat
The SDK signs every request with your consumer key and secret, so nothing about the provider reaches the device. You send a model and some messages.
import Bitmesh
let client = BitmeshClient(key: consumerKey, secret: consumerSecret)
let text = try await client.chatContent(
"Suggest three names for a hiking app.",
model: "google/gemma-3n-e4b-it"
)
import 'package:bitmesh_flutter_sdk/bitmesh_flutter_sdk.dart'; // Create one client and reuse it final client = BitmeshClient.fromKeys(consumerKey, consumerSecret); final completion = await client.chat.create(const ChatRequest( model: 'google/gemma-3n-e4b-it', messages: [ ChatMessage.user('Suggest three names for a hiking app.'), ], )); print(completion.text);
import os from bitmesh_ai import BitmeshClient # Optional third argument: timeout_seconds (default 30) client = BitmeshClient( os.environ["BITMESH_CONSUMER_KEY"], os.environ["BITMESH_CONSUMER_SECRET"], ) response = client.chat({ "model": "google/gemma-3n-e4b-it", "messages": [ {"role": "user", "content": "Suggest three names for a hiking app."} ], }) print(response["choices"][0]["message"]["content"])
use BitmeshAI\BitmeshClient; // Optional third argument: timeout in seconds (default 30) $client = new BitmeshClient('YOUR_CONSUMER_KEY', 'YOUR_CONSUMER_SECRET'); $response = $client->chat([ 'model' => 'google/gemma-3n-e4b-it', 'messages' => [ ['role' => 'user', 'content' => 'Suggest three names for a hiking app.'], ], ]); echo $response['choices'][0]['message']['content'];
Gate · what the key carries
Every key you create carries its own limits. Change them in the dashboard and the next request obeys — the app in the store never knows.
Ceilings
Give a key a token limit and a request limit when you create it. Usage counts up against both, so one runaway client can't drain the balance the rest of your users are sharing.
Scope
A key can be bound to a single model, so a key pulled out of a build can only ever call that one.
Lifecycle
Keys can carry an expiry date and an origin whitelist, and deleting one takes effect on the next call.
Ledger
Requests, tokens and spend are broken down by key and endpoint in the dashboard, with invoices for every top-up. You find out about a bad release from the chart, not the bill.
Requests · last 20 days
Upstream · where the request lands
Name the model in the request body and it goes there. Every model is priced per token, listed openly, and billed from the same balance.
Pay with prepaid credits — no subscription, no seat count. Top up from $5 and spend it across any model on the list.
Create a key, set its ceiling, and point your app at one endpoint. You can delete the key again before your next coffee.