Calling Workspace APIs
Use an OAuth access token to call Zafeguard's workspace endpoints — credit balance, workflows, API-key creation.
Calling Workspace APIs with an OAuth Access Token
Once your app has an access token (from the Quickstart flow), you can call Zafeguard's workspace-scoped endpoints exactly the way the consenting user would. Pass the token as a Authorization: Bearer … header; the workspace is implicit in the token's workspace_id claim, but you still pass it in the URL.
This page shows three common patterns. It is not a full API reference — only a representative slice. For the complete endpoint catalog, request types, and response shapes, contact support@zafeguard.com.
1. Get the workspace this token is for
The access token's payload carries the workspace your app is authorized for. Decode the JWT to read it:
import { jwtDecode } from "jwt-decode";
interface AccessTokenClaims {
readonly sub: string; // user id
readonly aud: string; // your client_id
readonly client_id: string; // your client_id (same value)
readonly workspace_id: string; // the workspace this token can act on
readonly scope: string; // space-separated granted scopes
readonly iat: number;
readonly exp: number;
}
const claims = jwtDecode<AccessTokenClaims>(accessToken);
const workspaceId = claims.workspace_id;
const grantedScopes = claims.scope.split(" ");Or verify the signature properly while you're at it (recommended for production — jwtDecode does not verify):
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://api.zafeguard.com/.well-known/jwks.json"),
);
const { payload } = await jwtVerify(accessToken, JWKS, {
issuer: "https://api.zafeguard.com",
audience: process.env.ZAFEGUARD_CLIENT_ID,
});
const workspaceId = payload.workspace_id as string;Each access token is bound to exactly one workspace via the workspace_id claim. If your app needs access to multiple workspaces, the user must authorize it for each one separately — every authorization yields its own token and its own refresh-token chain.
2. Read the workspace's credit balance
Required scope: workspace:read.billing (carved out of workspace:read so apps that only need workflow data do not also receive billing visibility)
curl https://api.zafeguard.com/v1/workspaces/$WORKSPACE_ID/billing \
-H "Authorization: Bearer $ACCESS_TOKEN"Response:
{
"credit": 5000,
"usage": 8500,
"total": 13500
}| Field | Meaning |
|---|---|
credit | Paid credit balance. Bought via top-up; never expires. |
usage | Plan-included credit balance. Resets on each plan renewal. |
total | credit + usage. The user's spendable balance right now. |
const res = await fetch(
`https://api.zafeguard.com/v1/workspaces/${workspaceId}/billing`,
{ headers: { Authorization: `Bearer ${accessToken}` } },
);
const { credit, usage, total } = await res.json();workspace:read.billing also unlocks GET /v1/workspaces/{workspaceId}/billing/transactions for the per-transaction history, plus payments, billing information, and subscription endpoints. Contact support for the full reference of those if you need them.
3. List the workspace's workflows
Required scope: workspace:read
curl "https://api.zafeguard.com/v1/workspaces/$WORKSPACE_ID/workflows?page=1&limit=20" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response (truncated):
{
"data": [
{ "id": "...", "name": "Daily payouts", "createdAt": "..." },
{ "id": "...", "name": "Treasury rebalance", "createdAt": "..." }
],
"total": 8,
"page": 1,
"totalPages": 1
}Pagination follows the same page + limit convention as every other list endpoint on the platform.
4. Create a named workspace API key
Required scope: workspace:api_keys
The narrow scope workspace:api_keys exists so apps that only need to mint a workspace API key on the user's behalf don't have to ask for workspace:admin (which would grant write + admin powers as a side effect). The endpoint returns the API key + Ed25519 secret exactly once — store them on the spot.
curl -X POST https://api.zafeguard.com/v1/workspaces/$WORKSPACE_ID/api-keys \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"label": "My provisioning agent",
"allowedWorkflowIds": [],
"expiresAt": null
}'Response:
{
"id": "8b1d…",
"apiKey": "ws_…",
"apiSecret": "<base64-encoded Ed25519 private key — shown ONCE>",
"label": "My provisioning agent",
"allowedWorkflowIds": [],
"expiresAt": null,
"createdAt": "..."
}After this request, your app holds an SDK-grade workspace API key (ws_…) and the matching Ed25519 secret. From that point on, it can call the SDK endpoints directly without the OAuth token — useful for unattended workflow execution where the user is no longer in the loop. Treat both values like production credentials; there is no way to recover them.
Only request workspace:api_keys if your app actually needs to provision a key. The user sees the scope on the consent screen and a "we'll mint an API key on your behalf" grant is a higher trust ask than a read-only one.
Error handling
| Status | Meaning |
|---|---|
401 | Access token missing, expired, or revoked. Refresh and retry. |
403 | Token does not have the scope this endpoint requires, or the workspace_id claim does not match the route's workspaceId. The token is bound to one workspace; calls against a different workspace are rejected even if the user is a member there. |
404 | Workspace or resource not found. |
429 | Rate limit. Check Retry-After. |
Use the granted-scope list from the access token to know which endpoints are reachable before you call them:
const scopes = (payload.scope as string).split(" ");
const canReadCredit = scopes.includes("workspace:read.billing") || scopes.includes("workspace:admin");
const canMintApiKey = scopes.includes("workspace:api_keys") || scopes.includes("workspace:admin");Full API reference
The endpoints shown here are a small sample. The full workspace API surface (workflow CRUD, environment variables, members, MCP settings, subscription management, …) is currently provided on request only. To obtain the complete reference for the surface your integration needs, please contact support@zafeguard.com with:
- Your
client_id(so we can confirm the app's allowed scopes). - A short description of the integration use case.
- Whether you need access to staging or production.