VisibleBase
Reference

AdminClient

How trusted environments manage products, issue user tokens, and maintain runtime env.

AdminClient should only run in trusted environments.

Typical cases:

  • your own product backend
  • local admin scripts
  • internal tools
  • CI or operations scripts

Do not expose admin_secret_key to browsers, public frontends, or uncontrolled clients.

Minimal example

import { AdminClient } from "@visiblebase/client";

const admin = new AdminClient({
  base_url: "https://base.example.com",
  admin_secret_key: process.env.VISIBLEBASE_ADMIN_SECRET_KEY,
});

If admin_secret_key is omitted, the SDK tries to read process.env.VISIBLEBASE_ADMIN_SECRET_KEY.

Typical call chain

Trusted backendCompletes login, auth, and business checks first.
AdminClientCalls /api/admin/products, /api/admin/tokens/apply, and /api/admin/env.
VisibleBaseVerifies admin_secret_key, then executes product, token, and env management actions.

products

list()

const items = await admin.products.list();

create()

const product = await admin.products.create({
  name: "Chrome Extension",
});

The response includes:

  • product_id
  • name
  • status
  • created_at
  • updated_at

pause() / activate()

await admin.products.pause({
  product_id: product.product_id,
});

await admin.products.activate({
  product_id: product.product_id,
});

Useful when you need to:

  • stop all user calls for one product
  • temporarily take a product offline
  • reopen calls for a product later

remove()

await admin.products.remove({
  product_id: product.product_id,
});

The current behavior is direct product deletion. Confirm that this matches your business expectation before using it.

tokens.apply()

This is the most common trusted-side action: issue a user_token for one user under one product.

const issued = await admin.tokens.apply({
  product_id: product.product_id,
  user_id: "user_123",
  metadata: {
    plan: "pro",
    org_id: "org_001",
  },
  ttl: "7d",
});

The response includes:

  • user_token
  • product_id
  • user_id
  • expires_at

ttl supports:

  • 30m
  • 1h
  • 7d
  • raw seconds
router.post("/login", async (c) => {
  const user_id = await login(c);

  const issued = await admin.tokens.apply({
    product_id: "prod_xxx",
    user_id,
    ttl: "7d",
  });

  return c.json({
    product_id: "prod_xxx",
    user_token: issued.user_token,
  });
});

That means:

  1. your backend handles user login first
  2. the trusted backend asks Base for user_token
  3. it returns product_id + user_token to the client
  4. the client calls Base through UserClient

env

admin.env manages runtime .env values.

list()

const envs = await admin.env.list();

upsert()

await admin.env.upsert({
  key: "OPENAI_API_KEY",
  value: "sk-xxx",
});

remove()

await admin.env.remove("OPENAI_API_KEY");

import()

await admin.env.import(`
OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://api.openai.com/v1
`);

These changes are written back to the .env file currently used by Base.

Error handling

When AdminClient receives a non-2xx HTTP response, it throws an Error with two extra fields:

  • status: the HTTP status code.
  • body: the raw response body from Base, usually {"error":"..."}.
try {
  await admin.tokens.apply({
    product_id: "prod_xxx",
    user_id: "user_123",
  });
} catch (error) {
  const status = error instanceof Error && "status" in error ? error.status : undefined;
  const body = error instanceof Error && "body" in error ? error.body : undefined;

  console.log(status, body);
}

Common statuses:

  • 401: admin_secret_key is missing or wrong.
  • 403: the target product is paused, so no token can be issued.
  • 404: the target product does not exist.
  • 500: Base is missing required config, or the admin action failed internally.

What AdminClient does not manage right now

These do not belong to AdminClient yet:

  • model configuration
  • direct edits to the models table
  • service handler registration

Those belong to:

  • the database layer
  • the Base runtime layer

On this page