API Reference

Namespaces

Namespaces let you organize workspaces into isolated groups with their own resource limits, spending quotas, and lifecycle controls. Every workspace belongs to a namespace - if you don't specify one, it goes into the default namespace.

TL;DR - Namespaces are how you build multi-tenant SaaS on Oblien. Assign one namespace per customer, team, or environment. Set resource caps (max vCPUs, RAM, workspaces) so no single tenant can exhaust your plan. Add spending quotas with overdraft policies so your agent or backend can manage plans cleanly - block requests, stop workspaces, or allow a grace zone before hard-cutting. Default quotas auto-apply to new namespaces so every new customer starts with the same limits.

Why namespaces

Without namespaces, every workspace shares one flat pool. That's fine for personal use, but breaks down as soon as you have:

  • Multiple customers - each needs isolated workspaces with separate spending limits
  • Multiple environments - production shouldn't compete with staging for resources
  • AI agents managing plans - agents need clean boundaries to enforce customer tiers
  • Cost attribution - you need to know which team or customer burned which credits

Namespaces solve all of these. Each namespace is a boundary for:

ConcernHow namespaces help
IsolationWorkspaces in different namespaces are fully separated
Resource capsMax vCPUs, RAM, disk, and workspace count per namespace
Spending quotasMonthly or daily credit limits per service per namespace
OverdraftGrace zone beyond the quota - block new requests or auto-stop workspaces
Default templatesAuto-apply quota configs to every new namespace
Lifecycle controlSuspend, activate, or stop all workspaces in a namespace at once
Audit trailActivity log per namespace - who did what, when
Usage analyticsPer-namespace credit burn, service breakdown, daily timeline

SaaS pattern

The most common pattern: your backend creates one namespace per customer. When a customer upgrades to a higher plan, you update the namespace's resource limits and quotas. Your AI agent creates workspaces inside the customer's namespace - it can never exceed the caps you set.

// Customer signs up → create their namespace
const ns = await client.namespaces.create({
  name: `customer-${customerId}`,
  type: 'production',
  resource_limits: {
    max_workspaces: plan.maxWorkspaces,
    max_vcpus: plan.maxCpus,
    max_ram_mb: plan.maxRamMb,
  },
});

// Set monthly spending limit
await client.namespaces.setQuota({
  namespace: ns.data.slug,
  service: 'sandbox',
  quotaLimit: plan.monthlyCredits,
  period: 'monthly',
  overdraft: plan.monthlyCredits * 0.1, // 10% grace
  onOverdraftAction: 'stop_workspaces',
});

// Agent creates workspaces inside the customer's namespace
const workspace = await client.workspaces.create({
  namespace: ns.data.slug,
  image: 'node-20',
  config: { cpus: 2, memory_mb: 2048 },
});

Agent control pattern

Agents can use namespaces to self-organize work. One namespace per project or per task category, with resource limits that prevent runaway costs:

// Agent creates a scoped environment for a task
const taskNs = await client.namespaces.create({
  name: `task-${taskId}`,
  type: 'staging',
  resource_limits: { max_workspaces: 5, max_vcpus: 4, max_ram_mb: 8192 },
});

// Run workspaces inside it
// ... when done, clean up everything at once
await client.namespaces.delete(taskNs.data.id, { deleteWorkspaces: true });

Create namespace

const res = await client.namespaces.create({
  name: 'production',
  type: 'production',
  description: 'Production customer workloads',
  tags: ['critical', 'auto-scale'],
  resource_limits: {
    max_workspaces: 50,
    max_vcpus: 8,
    max_ram_mb: 16384,
    max_disk_gb: 100,
  },
});
console.log(res.data.id, res.data.slug);
POST /namespaces
{
  "name": "production",
  "type": "production",
  "description": "Production customer workloads",
  "tags": ["critical", "auto-scale"],
  "resource_limits": {
    "max_workspaces": 50,
    "max_vcpus": 8,
    "max_ram_mb": 16384,
    "max_disk_gb": 100
  }
}
curl -X POST https://api.oblien.com/namespaces \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "production",
    "type": "production",
    "resource_limits": { "max_workspaces": 50, "max_vcpus": 8 }
  }'

Parameters

ParameterTypeRequiredDescription
namestringYesDisplay name
slugstringNoURL-safe identifier. Auto-generated from name if omitted
descriptionstringNoFree-text description
typestringNo"default", "production", "staging", "development", "testing"
isDefaultbooleanNoMark as the default namespace for new workspaces
metadataobjectNoArbitrary key-value data
tagsstring[]NoTags for filtering and organization
resource_limitsobjectNoResource caps (see below)

Resource limits

FieldTypeDescription
max_workspacesnumber | nullMax workspaces in namespace. null = inherit plan limit
max_vcpusnumber | nullMax vCPU cores per workspace
max_ram_mbnumber | nullMax memory per workspace in MB
max_disk_gbnumber | nullMax writable disk per workspace in GB

Resource limits are validated against your plan caps - you can't set a namespace limit higher than what your plan allows.

Response 201

{
  "success": true,
  "data": {
    "id": "ns_a1b2c3d4e5f6",
    "client_id": "cl_xyz",
    "name": "production",
    "slug": "production",
    "description": "Production customer workloads",
    "status": "active",
    "type": "production",
    "is_default": false,
    "metadata": {},
    "tags": ["critical", "auto-scale"],
    "resource_limits": {
      "max_workspaces": 50,
      "max_vcpus": 8,
      "max_ram_mb": 16384,
      "max_disk_gb": 100
    },
    "created_at": "2026-03-07T10:00:00Z",
    "updated_at": "2026-03-07T10:00:00Z",
    "last_active_at": null
  }
}

Errors

CodeStatusCause
MISSING_NAME400Name is required
NAMESPACE_LIMIT400Maximum 100 namespaces per account
DUPLICATE_SLUG409Slug already exists
RESOURCE_NOT_ALLOWED403Resource limits exceed plan caps

List namespaces

const { data, pagination } = await client.namespaces.list({
  status: 'active',
  search: 'prod',
  sortBy: 'created_at',
  sortOrder: 'DESC',
  limit: 20,
  offset: 0,
});
console.log(`${pagination.total} namespaces total`);
GET /namespaces?status=active&search=prod&limit=20&sortBy=created_at
curl "https://api.oblien.com/namespaces?status=active&limit=20" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Query parameters

ParameterTypeDefaultDescription
limitnumber50Results per page (max 100)
offsetnumber0Pagination offset
statusstring-Filter: "active", "inactive", "suspended"
typestring-Filter by namespace type
searchstring-Search name and slug
sortBystring"created_at""name", "created_at", "updated_at"
sortOrderstring"DESC""ASC" or "DESC"

Response

{
  "success": true,
  "data": [ /* NamespaceData[] */ ],
  "pagination": { "total": 42, "limit": 20, "offset": 0 }
}

Get namespace

Retrieve a namespace by ID or slug.

// By slug
const ns = await client.namespaces.get('production');

// By ID
const ns = await client.namespaces.get('ns_a1b2c3d4e5f6');
GET /namespaces/:identifier
curl "https://api.oblien.com/namespaces/production" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "data": { /* NamespaceData */ }
}

Update namespace

const updated = await client.namespaces.update('ns_a1b2c3d4e5f6', {
  description: 'Updated description',
  tags: ['production', 'us-east'],
  resource_limits: { max_workspaces: 100 },
});
PUT /namespaces/:id
{
  "description": "Updated description",
  "tags": ["production", "us-east"],
  "resource_limits": { "max_workspaces": 100 }
}
curl -X PUT "https://api.oblien.com/namespaces/ns_a1b2c3d4e5f6" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "description": "Updated", "resource_limits": { "max_workspaces": 100 } }'

Updatable fields

FieldTypeDescription
namestringDisplay name
descriptionstringDescription
statusstring"active", "inactive", "suspended"
typestringNamespace type
tagsstring[]Tags
metadataobjectKey-value metadata
resource_limitsobjectResource caps

Delete namespace

Permanently delete a namespace. Optionally delete all workspaces in it.

// Delete namespace only (workspaces remain, moved to default)
await client.namespaces.delete('ns_a1b2c3d4e5f6');

// Delete namespace AND all its workspaces
await client.namespaces.delete('ns_a1b2c3d4e5f6', { deleteWorkspaces: true });
DELETE /namespaces/:id
{ "deleteWorkspaces": true }
curl -X DELETE "https://api.oblien.com/namespaces/ns_a1b2c3d4e5f6" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "deleteWorkspaces": true }'

Parameters

ParameterTypeDefaultDescription
deleteWorkspacesbooleanfalseDelete all workspaces in the namespace

When deleteWorkspaces is true, all workspaces, their data, snapshots, and logs are permanently destroyed. This is irreversible.


Suspend namespace

Suspend a namespace - sets status to suspended and stops all running workspaces. No new workspaces can be created in a suspended namespace.

await client.namespaces.suspend('ns_a1b2c3d4e5f6');
POST /namespaces/:id/suspend
curl -X POST "https://api.oblien.com/namespaces/ns_a1b2c3d4e5f6/suspend" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "message": "Namespace suspended",
  "stoppedWorkspaces": 3
}

Activate namespace

Reactivate a suspended namespace. Sets status back to active. Workspaces are not automatically restarted - you need to start them individually.

await client.namespaces.activate('ns_a1b2c3d4e5f6');
POST /namespaces/:id/activate
curl -X POST "https://api.oblien.com/namespaces/ns_a1b2c3d4e5f6/activate" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Stop all workspaces

Stop every running workspace in the namespace without changing namespace status. Useful for cost control - stop the workloads but keep the namespace active for new creations.

await client.namespaces.stopWorkspaces('ns_a1b2c3d4e5f6');
POST /namespaces/:id/stop-workspaces
curl -X POST "https://api.oblien.com/namespaces/ns_a1b2c3d4e5f6/stop-workspaces" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Activity log

Get the audit trail for a namespace - creation, updates, suspensions, and other lifecycle events.

const res = await client.namespaces.activity('ns_a1b2c3d4e5f6', {
  limit: 20,
  offset: 0,
});
GET /namespaces/:id/activity?limit=20&offset=0
curl "https://api.oblien.com/namespaces/ns_a1b2c3d4e5f6/activity?limit=20" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Response

{
  "success": true,
  "data": [
    {
      "id": 1,
      "namespace_id": "ns_a1b2c3d4e5f6",
      "user_id": "cl_xyz",
      "action": "created",
      "description": "Namespace created",
      "changes": { "name": "production", "type": "production" },
      "ip_address": "203.0.113.1",
      "created_at": "2026-03-07T10:00:00Z"
    },
    {
      "id": 2,
      "action": "updated",
      "description": "Namespace updated",
      "changes": { "description": ["", "Production workloads"] },
      "created_at": "2026-03-07T10:05:00Z"
    }
  ]
}

Usage statistics

Get credit usage for a namespace - daily breakdown by service.

const res = await client.namespaces.usage('ns_a1b2c3d4e5f6', {
  service: 'sandbox',
  days: 30,
});
GET /namespaces/:id/usage?service=sandbox&days=30
curl "https://api.oblien.com/namespaces/ns_a1b2c3d4e5f6/usage?days=30" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Query parameters

ParameterTypeDefaultDescription
servicestring-Filter by service ("sandbox", "ai", "deployment", etc.)
daysnumber30Lookback period

Response

{
  "success": true,
  "data": {
    "usage": [
      { "date": "2026-03-06", "service": "sandbox", "total_requests": 150, "total_credits": 42.5 },
      { "date": "2026-03-07", "service": "sandbox", "total_requests": 200, "total_credits": 55.0 }
    ],
    "summary": [
      { "service": "sandbox", "total_requests": 350, "total_credits": 97.5 }
    ],
    "quotas": [
      {
        "namespace": "production",
        "service": "sandbox",
        "quota_limit": 500,
        "quota_used": 97.5,
        "overdraft": 50,
        "on_overdraft_action": "stop_workspaces",
        "period": "monthly"
      }
    ]
  }
}

Quotas & Overdraft

Quotas let you set spending limits on a per-namespace, per-service basis. Combined with overdraft policies, they give you fine-grained control over how much each namespace can spend and what happens when it hits the limit.

How quotas work

                 quota_limit                  quota_limit + overdraft
                   │                                 │
   ────────────────┼─────────────────────────────────┼──────────────
   NORMAL ZONE     │  OVERDRAFT ZONE (grace)         │  HARD BLOCK
   (allowed)       │  (allowed, with warning)         │  (rejected)
  1. Normal zone - quota_used ≤ quota_limit. Requests proceed normally.
  2. Overdraft zone - quota_limit < quota_used ≤ quota_limit + overdraft. Requests still succeed, but the API returns a warning header. This is your grace buffer.
  3. Hard block - quota_used > quota_limit + overdraft. New requests are rejected with HTTP 402. If onOverdraftAction is "stop_workspaces", all running workspaces in the namespace are also stopped.

Overdraft actions

ActionBehavior
blockReject new credit-consuming requests (HTTP 402). Running workspaces continue.
stop_workspacesReject new requests and stop all running workspaces in the namespace.

Set quota

await client.namespaces.setQuota({
  namespace: 'production',
  service: 'sandbox',
  quotaLimit: 500,
  period: 'monthly',
  overdraft: 50,
  onOverdraftAction: 'stop_workspaces',
});
POST /credits/namespace-quota
{
  "namespace": "production",
  "service": "sandbox",
  "quotaLimit": 500,
  "period": "monthly",
  "overdraft": 50,
  "onOverdraftAction": "stop_workspaces"
}
curl -X POST https://api.oblien.com/credits/namespace-quota \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "namespace": "production",
    "service": "sandbox",
    "quotaLimit": 500,
    "period": "monthly",
    "overdraft": 50,
    "onOverdraftAction": "stop_workspaces"
  }'

Parameters

ParameterTypeRequiredDescription
namespacestringYesNamespace slug
servicestringYesService name: "sandbox", "ai", "deployment", etc.
quotaLimitnumberYesCredit limit (max 10,000,000)
periodstringNo"daily", "monthly", or "unlimited". Default: "monthly"
overdraftnumberNoGrace zone credits beyond the limit. Default: 0
onOverdraftActionstringNo"block" or "stop_workspaces". Default: "block"

Reset quota

Reset quota_used back to zero for a namespace + service. Useful for manual resets or when you want to give a customer a fresh start mid-period.

await client.namespaces.resetQuota({
  namespace: 'production',
  service: 'sandbox',
});
POST /credits/reset-quota
{ "namespace": "production", "service": "sandbox" }
curl -X POST https://api.oblien.com/credits/reset-quota \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "namespace": "production", "service": "sandbox" }'

List with quota data

List namespaces enriched with spending and quota information - total credits consumed, active quotas, and per-service breakdown.

const res = await client.namespaces.listWithQuotas({
  status: 'active',
  limit: 20,
});
GET /credits/namespaces?status=active&limit=20
curl "https://api.oblien.com/credits/namespaces?status=active&limit=20" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Get detailed quota info

Comprehensive namespace financial details - quotas, usage stats, service breakdown, daily timeline, and recent transactions. Up to 90 days lookback.

const details = await client.namespaces.getDetails('production', { days: 30 });
GET /credits/namespaces/:namespace?days=30
curl "https://api.oblien.com/credits/namespaces/production?days=30" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Default Quotas

Default quotas are templates that auto-apply to new namespaces. When a namespace first consumes credits for a service and no quota exists, the system checks for a default config. If autoApply is enabled, it creates a quota from the template - including overdraft settings.

This is how you set a baseline for every customer namespace without configuring each one manually.

Set default quota

await client.namespaces.setDefaultQuota({
  service: 'sandbox',
  quotaLimit: 100,
  period: 'monthly',
  overdraft: 10,
  onOverdraftAction: 'block',
  autoApply: true,
});
POST /credits/defaults
{
  "level": "namespace",
  "service": "sandbox",
  "quotaLimit": 100,
  "period": "monthly",
  "overdraft": 10,
  "onOverdraftAction": "block",
  "autoApply": true
}
curl -X POST https://api.oblien.com/credits/defaults \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "level": "namespace",
    "service": "sandbox",
    "quotaLimit": 100,
    "period": "monthly",
    "autoApply": true
  }'

Parameters

ParameterTypeRequiredDescription
servicestringYesService name
quotaLimitnumberYesCredit limit
periodstringNo"daily", "monthly", "unlimited"
overdraftnumberNoGrace zone. Default: 0
onOverdraftActionstringNo"block" or "stop_workspaces"
autoApplybooleanNoAuto-apply to new namespaces. Default: true

Get default quota

const config = await client.namespaces.getDefaultQuota('sandbox');
GET /credits/defaults?level=namespace&service=sandbox
curl "https://api.oblien.com/credits/defaults?level=namespace&service=sandbox" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Get all default quotas

const configs = await client.namespaces.getAllDefaultQuotas();
GET /credits/defaults/all?level=namespace
curl "https://api.oblien.com/credits/defaults/all?level=namespace" \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET"

Delete default quota

await client.namespaces.deleteDefaultQuota('sandbox');
DELETE /credits/defaults
{ "level": "namespace", "service": "sandbox" }
curl -X DELETE https://api.oblien.com/credits/defaults \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "level": "namespace", "service": "sandbox" }'

Toggle auto-apply

// Enable auto-apply
await client.namespaces.toggleDefaultQuotaAutoApply('sandbox', true);

// Disable auto-apply
await client.namespaces.toggleDefaultQuotaAutoApply('sandbox', false);
POST /credits/defaults/toggle-auto-apply
{ "level": "namespace", "service": "sandbox", "autoApply": true }
curl -X POST https://api.oblien.com/credits/defaults/toggle-auto-apply \
  -H "X-Client-ID: $OBLIEN_CLIENT_ID" \
  -H "X-Client-Secret: $OBLIEN_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "level": "namespace", "service": "sandbox", "autoApply": true }'

Namespace object

All namespace endpoints return this shape:

interface NamespaceData {
  id: string;                     // "ns_a1b2c3d4e5f6"
  client_id: string;
  name: string;
  slug: string;                   // URL-safe, unique per account
  description?: string;
  status: 'active' | 'inactive' | 'suspended';
  type: string;                   // "default", "production", "staging", etc.
  is_default: boolean;
  metadata?: Record<string, unknown>;
  tags?: string[];
  resource_limits?: {
    max_workspaces?: number | null;
    max_vcpus?: number | null;
    max_ram_mb?: number | null;
    max_disk_gb?: number | null;
  } | null;
  created_at: string;
  updated_at: string;
  last_active_at?: string;
}

Quota object

interface NamespaceQuota {
  id: number;
  client_id: string;
  namespace: string;              // Matches namespace slug
  service: string;                // "sandbox", "ai", "deployment", etc.
  quota_limit: number | null;     // null = unlimited
  quota_used: number;
  overdraft: number;              // Grace zone beyond limit
  on_overdraft_action: 'block' | 'stop_workspaces';
  period: 'daily' | 'monthly' | 'unlimited';
  period_start?: string;
  period_end?: string;
  enabled: boolean;
  created_at: string;
  updated_at: string;
}

Limits

LimitValue
Max namespaces per account100
Max quota limit value10,000,000 credits
Max activity log page size100
Valid overdraft actionsblock, stop_workspaces
Valid statusesactive, inactive, suspended
Valid quota periodsdaily, monthly, unlimited