Skip to content

Suspend and reactivate a user

Deny new model requests before provider invocation and restore the same workspace access later.

Updated View as Markdown
Audience: application-backendPlane: adminAuth: management-keyStatus: beta

Goal

暂停一名应用用户的新请求,然后恢复同一条 WorkspaceAccess。

Outcome

暂停期间的新请求返回 workspace_access_denied,并在调用 Provider 前结束;恢复后继续使用同一 Access、Plan、Period 和历史 Usage/Audit。

Prerequisites

  • Access ID、Management Key,以及该用户现有的 Gateway Base URL、Inference Key 和原始模型名。

Lifecycle note

暂停是 Access 状态变化,不是删除用户、撤销所有历史事实或创建新 QuotaPeriod。Billing Connector 也只能异步投影这个状态,Gateway 不同步查询支付 Provider。

1. Exercise both state transitions

suspend-reactivate-access.mjsjs
const adminBaseUrl = required('NEXUS_ADMIN_BASE_URL').replace(/\/$/, '');
const managementKey = required('NEXUS_MANAGEMENT_KEY');
const accessId = required('NEXUS_ACCESS_ID');
const gatewayBaseUrl = required('NEXUS_GATEWAY_BASE_URL').replace(/\/$/, '');
const inferenceKey = required('NEXUS_API_KEY');
const model = required('NEXUS_MODEL');

async function setAccess(action) {
  const response = await fetch(`${adminBaseUrl}/admin/v1/workspace-accesses/${accessId}/${action}`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${managementKey}` },
  });
  if (!response.ok) throw new Error(`${action} failed: ${response.status} ${await response.text()}`);
  return response.json();
}

async function callModel() {
  return fetch(`${gatewayBaseUrl}/chat/completions`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${inferenceKey}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ model, messages: [{ role: 'user', content: 'Access state smoke test.' }] }),
  });
}

const suspended = await setAccess('suspend');
const denied = await callModel();
const deniedBody = await denied.json();
if (denied.status !== 403 || deniedBody.error?.code !== 'workspace_access_denied') {
  throw new Error(`expected workspace_access_denied, received ${denied.status}`);
}

const active = await setAccess('activate');
const restored = await callModel();
if (!restored.ok) throw new Error(`reactivated request failed: ${restored.status} ${await restored.text()}`);
console.log(
  JSON.stringify({
    suspended: suspended.status,
    active: active.status,
    requestId: restored.headers.get('x-nexus-request-id'),
  }),
);

function required(name) {
  const value = process.env[name]?.trim();
  if (!value) throw new Error(`${name} is required`);
  return value;
}

Check your work

暂停响应为 SUSPENDED,恢复响应为 ACTIVE。暂停请求没有 Provider Attempt;恢复请求有新的 Invocation,之前的 UsageEvent 与 Audit Log 仍可查询。

Failure modes

  • invalid_inference_key:凭证本身不可用,与 Access 暂停不同。
  • 恢复后仍拒绝:检查 accessEndsAt、当前 QuotaPeriod、ProviderConnection 状态和 Quota。
  • Billing 状态很快再次覆盖:先确认 Billing Connector 的订阅投影事实,而不是在 Gateway 加例外。

Security notes

暂停不会泄露用户是否属于其他 Workspace。不要通过删除审计/用量记录来“完成”暂停。

Next steps

Troubleshooting 判断错误发生在哪一层。

Navigation

Type to search…

↑↓ navigate↵ selectEsc close