Bridge by Wekoya / Docs / Get started

Browser and mobile apps

Updated

Your API key must stay on your server. To let a web page or a phone app call Bridge directly (for chat, voice or pictures without a round trip through your server), give it a guest key: a key your server makes for one user, that lasts minutes, can only do what you allow, and stops at the amount you set.

  1. Your app asks your server Your signed-in user's app asks your own backend for a key.
  2. Your server asks Bridge With your real key, it calls POST /v1/guest-keys.
  3. Bridge returns a guest key Short-lived, limited to what you allowed, capped in naira.
  4. The app calls Bridge Directly, with the guest key, like any API key.

Make a guest key

On your server, with your API key:

curl https://wekoya-api.wekoya.tech/v1/guest-keys \
  -H "Authorization: Bearer $WEKOYA_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "expires_in": 900,
    "max_spend": 50,
    "uses": ["chat", "speech"],
    "end_user": "user_123",
    "origins": ["https://app.example.com"]
  }'
{
  "object": "guest_key",
  "id": "8f1c2d7e-…",
  "key": "wk_guest_…",
  "expires_at": "2026-10-10T12:15:00.000Z",
  "max_spend": 50,
  "currency": "NGN",
  "uses": ["chat", "speech"],
  "end_user": "user_123",
  "origins": ["https://app.example.com"]
}

The guest key is shown once. Give it to the app; don't store it. When it runs out or expires, the app asks your server for a new one.

Use it in the app

A route on your server hands keys to your signed-in users:

// On your server: a route your signed-in users call to get a guest key.
app.post("/api/bridge-key", requireSignedIn, async (req, res) => {
  const response = await fetch("https://wekoya-api.wekoya.tech/v1/guest-keys", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.WEKOYA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ expires_in: 900, max_spend: 50, uses: ["chat"], end_user: req.user.id }),
  })
  const guest = await response.json()
  if (!response.ok) return res.status(502).json({ error: guest.error?.message ?? "Could not get a key" })
  res.json({ key: guest.key, expires_at: guest.expires_at })
})

The app then uses the guest key exactly like an API key:

// In the browser: use the guest key like an API key.
import OpenAI from "openai"

const { key } = await fetch("/api/bridge-key", { method: "POST" }).then((r) => r.json())

const client = new OpenAI({
  baseURL: "https://wekoya-api.wekoya.tech/v1",
  apiKey: key,
  dangerouslyAllowBrowser: true, // safe here: the key is short-lived and limited
})

const reply = await client.chat.completions.create({
  model: "gemini-3.8-flash",
  messages: [{ role: "user", content: "Suggest a name for my bakery" }],
})

For live audio, a browser WebSocket cannot send a key, so the app gets a one-time ticket first (allow live in uses):

// Live audio from a browser. A WebSocket cannot send headers, so get a one-time ticket first.
const ticket = await fetch("https://wekoya-api.wekoya.tech/v1/audio/live/tickets", {
  method: "POST",
  headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
  body: JSON.stringify({ model: "nova-3" }),
}).then((r) => r.json())

const socket = new WebSocket(ticket.url) // the ticket works once, within a minute

What you can set

Stopping one early

When a user signs out, revoke their key. Revoking the API key that made guest keys stops all of them, and stops any live audio they started.

curl -X POST https://wekoya-api.wekoya.tech/v1/guest-keys/$GUEST_KEY_ID/revoke \
  -H "Authorization: Bearer $WEKOYA_API_KEY"

Make guest keys with a key that has no spending limit of its own (each guest key has its own cap), and keep that key on your server. GET /v1/guest-keys lists the ones still working, with what each has spent.