API Keys
Users can generate their own API keys through Tribe, then use those keys to authenticate against your backend. Tribe handles creation, listing, and deletion on the client side. Your backend verifies incoming keys by checking them against the Tribe API.
Create an API key
Section titled “Create an API key”import { Tribe } from "@tribecloud/sdk";
const result = await tribe.createApiKey("My API Key", ["read", "write"]);// result.id — key ID// result.key — the full API key (only returned once!)// result.name — "My API Key"// result.scopes — ["read", "write"]List API keys
Section titled “List API keys”const keys = await tribe.listApiKeys();// [{ id, name, keyPrefix, scopes, createdAt }]Delete an API key
Section titled “Delete an API key”await tribe.deleteApiKey(keyId);API key management UI (React)
Section titled “API key management UI (React)”function ApiKeyManager() { const [keys, setKeys] = useState([]); const [newKeyName, setNewKeyName] = useState(""); const [createdKey, setCreatedKey] = useState(null);
useEffect(() => { tribe.listApiKeys().then(setKeys); }, []);
const create = async () => { const result = await tribe.createApiKey(newKeyName, ["read", "write"]); setCreatedKey(result.key); // Show the full key — only chance! setNewKeyName(""); setKeys(await tribe.listApiKeys()); };
const remove = async (id) => { await tribe.deleteApiKey(id); setKeys((k) => k.filter((key) => key.id !== id)); };
return ( <div> <h2>API Keys</h2>
{createdKey && ( <div role="alert"> <strong>Your new API key:</strong> <code>{createdKey}</code> <p>Copy this now — you won't be able to see it again.</p> <button onClick={() => setCreatedKey(null)}>Done</button> </div> )}
<div> <input value={newKeyName} onChange={(e) => setNewKeyName(e.target.value)} placeholder="Key name" /> <button onClick={create}>Create Key</button> </div>
<ul> {keys.map((key) => ( <li key={key.id}> <strong>{key.name}</strong> — {key.keyPrefix}... <span> ({key.scopes.join(", ")})</span> <button onClick={() => remove(key.id)}>Delete</button> </li> ))} </ul> </div> );}Scopes
Section titled “Scopes”Scopes are arbitrary strings representing permission levels. Tribe stores them alongside each key, but the actual enforcement is up to your backend. Some common patterns:
["read"]for read-only access["read", "write"]for full access["admin"]for administrative operations
Verifying API keys on your backend
Section titled “Verifying API keys on your backend”When a user sends an API key to your backend, you can verify it by calling the Tribe verification endpoint. This checks the key against Tribe’s stored hashes and returns the associated user and scopes if the key is valid.
// POST https://api.tribe.utopian.build/api-key/verifyconst response = await fetch("https://api.tribe.utopian.build/api-key/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ siteId: process.env.TRIBE_SITE_ID, apiKey: keyFromRequest, }),});const result = await response.json();// result.valid — boolean// result.userId — the user who owns this key// result.scopes — ["read", "write"]Here’s how you might wire this into Express middleware:
async function verifyApiKey(req, res, next) { const apiKey = req.headers["x-api-key"]; if (!apiKey) return res.status(401).json({ error: "API key required" });
const result = await fetch("https://api.tribe.utopian.build/api-key/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ siteId: process.env.TRIBE_SITE_ID, apiKey, }), }).then((r) => r.json());
if (!result.valid) return res.status(401).json({ error: "Invalid API key" });
req.apiKeyUser = result; next();}
// Use it on protected routesapp.get("/api/data", verifyApiKey, (req, res) => { const { userId, scopes } = req.apiKeyUser; if (!scopes.includes("read")) { return res.status(403).json({ error: "Insufficient scope" }); } // ... serve data});