워크스페이스
워크스페이스 (Workspaces)
워크스페이스는 조직 안에서 API 사용량을 정리하는 방법을 제공해요. 워크스페이스를 사용해 서로 다른 프로젝트, 환경, 팀을 분리하면서도 중앙 집중식 청구와 관리를 유지할 수 있어요.
출처: 문서
본문
워크스페이스 작동 방식
모든 조직에는 이름을 바꾸거나 보관하거나 삭제할 수 없는 기본 워크스페이스(Default Workspace) 가 있어요. 추가 워크스페이스를 만들면 각각에 구성원, 서비스 계정, API 키, 리소스 제한을 할당할 수 있어요.
주요 특징:
- 워크스페이스 식별자는
wrkspc_접두사를 사용해요(예:wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ). - 조직당 기본 최대 100개 워크스페이스(보관된 워크스페이스는 집계되지 않음). 더 필요하면 계정 팀에 연락하세요.
- 기본 워크스페이스는 다른 워크스페이스처럼
wrkspc_ID를 가져요(anthropic-workspace-id응답 헤더에 반환되고 Get Workspace가 받아들임). 하지만 List Workspaces 결과에는 나타나지 않고, API 키·사용량 보고·비용 보고는 그workspace_id에null을 보여줘요(모든 워크스페이스 키도 마찬가지. API 키의scope필드가 구분해 주고, 기본 워크스페이스에 바인딩된 키에는 실제 ID를 나른다). - API 키는 단일 워크스페이스에 범위를 제한할 수 있어요. 그 경우 그 워크스페이스 안의 리소스에만 접근할 수 있어요. 일부 API 키는 여러 워크스페이스에 걸쳐 권한을 부여받을 수 있고, 그 워크스페이스 안의 리소스에 접근하도록 워크스페이스 ID 헤더를 제공해요.
Claude Code 워크스페이스
조직의 구성원이 처음 Claude Code에 Claude Console 계정으로 로그인하면, Anthropic이 자동으로 조직에 Claude Code 워크스페이스를 만들고 그 구성원을 추가해요. 이후 Claude Code에 로그인하는 모든 구성원도 같은 방식으로 추가돼요.
Claude Code 워크스페이스는 Claude Code 트래픽을 다른 API 워크로드와 분리해요:
- Claude Code는 로그인 시 이 워크스페이스에서 사용자별 API 키를 발행해요. Console에서 수동으로 키를 만들 수는 없어요.
- Claude Code 키는 워크스페이스 키와 달리, 소유자가 워크스페이스나 조직에서 제거되면 작동을 멈춰요.
- Claude Code 사용량은 별도로 속도 제한되며, 관리자는 Settings > Workspaces 아래에서 조직 제한에서 그 몫을 상한으로 정할 수 있어요.
- 사용자별 월 지출 한도를 지원하는 유일한 워크스페이스예요.
경고 Claude Code 워크스페이스를 보관하면 조직 전체에서 Console 청구를 통한 Claude Code 로그인이 비활성화돼요.
워크스페이스 역할과 권한
구성원은 각 워크스페이스에서 다른 역할을 가질 수 있어 세밀한 접근 제어가 가능해요.
| 역할 | 권한 |
|---|---|
| Workspace User | 플레이그라운드만 사용 |
| Workspace Limited Developer | API 키를 만들고 관리하고 API를 사용. 세션 추적 보기나 파일 다운로드에는 접근 불가. |
| Workspace Developer | API 키를 만들고 관리하고 API를 사용 |
| Workspace Admin | 워크스페이스 설정과 구성원에 대한 전체 제어 |
| Workspace Billing | 워크스페이스 청구 정보 보기(조직 청구 역할에서 상속) |
역할 상속
- 조직 관리자(admins) 는 모든 워크스페이스에 Workspace Admin 접근을 자동으로 받아요.
- 조직 청구 구성원(billing members) 은 모든 워크스페이스에 Workspace Billing 접근을 자동으로 받아요.
- 조직 사용자와 개발자는 각 워크스페이스에 명시적으로 추가되어야 해요.
- 서비스 계정은 Settings → Service accounts의 서비스 계정 페이지나 워크스페이스의 Service accounts 탭에서 워크스페이스에 추가돼요.
참고 Workspace Billing 역할은 수동으로 할당할 수 없어요. 조직 청구 역할을 가짐으로써 상속되는 것이에요.
워크스페이스 관리
참고 조직 관리자만 워크스페이스를 만들 수 있어요. 조직 사용자와 개발자는 관리자가 워크스페이스에 추가해야 해요.
Console 사용하기
Claude Console에서 워크스페이스를 만들고 관리하세요.
워크스페이스 만들기
- 워크스페이스 설정 열기 Claude Console에서 Settings > Workspaces로 가세요.
- 워크스페이스 만들기 Create workspace를 클릭하세요.
- 워크스페이스 구성 워크스페이스 이름을 입력하고 시각적 식별용 색상을 선택하세요.
- 워크스페이스 만들기 Create를 클릭해 마무리하세요.
팁 Console에서 워크스페이스 사이를 전환하려면 왼쪽 상단의 Workspaces 선택기를 사용하세요.
워크스페이스 세부사항 편집
워크스페이스의 이름이나 색상을 수정하려면:
- 목록에서 워크스페이스를 선택하세요.
- 줄임표 메뉴(...)를 클릭하고 Edit details를 선택하세요.
- 이름이나 색상을 업데이트하고 변경을 저장하세요.
참고 기본 워크스페이스는 이름을 바꾸거나 삭제할 수 없어요.
워크스페이스에 구성원 추가
- 워크스페이스의 Members 탭으로 이동하세요.
- Add to Workspace를 클릭하세요.
- 조직 구성원을 선택하고 워크스페이스 역할을 할당하세요.
- 추가를 확인하세요.
구성원을 제거하려면 이름 옆의 휴지통 아이콘을 클릭하세요.
참고 조직 관리자와 청구 구성원은 그 조직 역할을 보유하는 동안 워크스페이스에서 제거할 수 없어요.
워크스페이스 제한 설정
각 워크스페이스의 설정은 이 두 탭에 걸쳐 나뉘어요:
- Rate limits: Rate limits 탭에서 모델 티어별로 분당 요청, 입력 토큰, 출력 토큰에 대한 제한을 설정하세요.
- Spend limits: Spend limits 탭에서 월 지출을 상한으로 정하고 지출이 특정 임계값에 닿을 때 알림을 구성하세요.
워크스페이스 보관
워크스페이스를 보관하려면 줄임표 메뉴(...)를 클릭하고 Archive를 선택하세요. 보관은:
- 보고용 과거 데이터를 보존해요.
- 워크스페이스를 비활성화하고 그것을 위해 만든 모든 API 키를 보관해요.
- 되돌릴 수 없어요.
경고 워크스페이스를 보관하면 그 워크스페이스를 위해 만든 모든 API 키가 몇 초 안에 보관되어(Admin API에 보관된 것으로 계속 나열됨) 다중 워크스페이스 키가 더 이상 그 안에서 행동할 수 없어요. 이 작업은 되돌릴 수 없어요. Claude Code 워크스페이스를 보관하면 조직 구성원이 더 이상 Console 청구를 통해 Claude Code에 로그인할 수 없어요.
Admin API 사용하기
Admin API로 워크스페이스를 프로그래밍 방식으로 관리하세요.
참고 Admin API 엔드포인트는 Admin API 키,
org:adminOAuth 토큰, 또는 특정 워크스페이스에 범위가 제한되지 않은 개인·서비스 계정 키를 받아요. 워크스페이스 키는 거기서 작동하지 않아요. 인증 참고.
다음 SDK·CLI 예시는 ANTHROPIC_API_KEY 환경 변수에서 Admin API 키를 읽는 기본 클라이언트를 구성해요. SDK는 client.beta.organization.workspaces 아래에 이 엔드포인트들을 노출해요. SDK 목록 메서드는 필요에 따라 추가 페이지를 가져오므로 limit은 페이지 크기를 설정해요. PHP·Ruby·curl 예시는 한 페이지를 반환해요.
워크스페이스 만들기:
curl -X POST "https://api.anthropic.com/v1/organizations/workspaces" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"name": "Production"}'
ant beta:organization:workspaces create --name Production
client = anthropic.Anthropic()
workspace = client.beta.organization.workspaces.create(name="Production")
print(f"id: {workspace.id}")
print(f"name: {workspace.name}")
const client = new Anthropic();
const workspace = await client.beta.organization.workspaces.create({ name: "Production" });
console.log(`id: ${workspace.id}`);
console.log(`name: ${workspace.name}`);
AnthropicClient client = new();
var workspace = await client.Beta.Organization.Workspaces.Create(new()
{
Name = "Production"
});
Console.WriteLine($"id: {workspace.ID}");
Console.WriteLine($"name: {workspace.Name}");
client := anthropic.NewClient()
workspace, err := client.Beta.Organization.Workspaces.New(context.Background(), anthropic.BetaOrganizationWorkspaceNewParams{
Name: "Production",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("id: %s\n", workspace.ID)
fmt.Printf("name: %s\n", workspace.Name)
import com.anthropic.models.beta.organization.workspaces.WorkspaceCreateParams;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
var params = WorkspaceCreateParams.builder()
.name("Production")
.build();
var workspace = client.beta().organization().workspaces().create(params);
IO.println("id: " + workspace.id());
IO.println("name: " + workspace.name());
}
$client = new Client();
$workspace = $client->beta->organization->workspaces->create(
name: 'Production',
);
echo "id: {$workspace->id}\n";
echo "name: {$workspace->name}\n";
client = Anthropic::Client.new
workspace = client.beta.organization.workspaces.create(name: "Production")
puts "id: #{workspace.id}"
puts "name: #{workspace.name}"
워크스페이스 나열하기:
curl "https://api.anthropic.com/v1/organizations/workspaces?limit=10&include_archived=false" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01"
ant beta:organization:workspaces list --limit 10 --include-archived=false
client = anthropic.Anthropic()
workspaces = client.beta.organization.workspaces.list(limit=10, include_archived=False)
for workspace in workspaces:
print(f"{workspace.id}: {workspace.name}")
const client = new Anthropic();
const workspaces = await client.beta.organization.workspaces.list({
limit: 10,
include_archived: false
});
for await (const workspace of workspaces) {
console.log(`${workspace.id}: ${workspace.name}`);
}
AnthropicClient client = new();
var workspaces = await client.Beta.Organization.Workspaces.List(new()
{
Limit = 10,
IncludeArchived = false
});
await foreach (var workspace in workspaces.Paginate())
{
Console.WriteLine($"{workspace.ID}: {workspace.Name}");
}
client := anthropic.NewClient()
workspaces := client.Beta.Organization.Workspaces.ListAutoPaging(context.Background(), anthropic.BetaOrganizationWorkspaceListParams{
Limit: anthropic.Int(10),
IncludeArchived: anthropic.Bool(false),
})
for workspaces.Next() {
workspace := workspaces.Current()
fmt.Printf("%s: %s\n", workspace.ID, workspace.Name)
}
if err := workspaces.Err(); err != nil {
log.Fatal(err)
}
import com.anthropic.models.beta.organization.workspaces.WorkspaceListParams;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
var params = WorkspaceListParams.builder()
.limit(10)
.includeArchived(false)
.build();
var workspaces = client.beta().organization().workspaces().list(params);
for (var workspace : workspaces.autoPager()) {
IO.println(workspace.id() + ": " + workspace.name());
}
}
$client = new Client();
$workspaces = $client->beta->organization->workspaces->list(
limit: 10,
includeArchived: false,
);
foreach ($workspaces->getItems() as $workspace) {
echo "{$workspace->id}: {$workspace->name}\n";
}
client = Anthropic::Client.new
workspaces = client.beta.organization.workspaces.list(limit: 10, include_archived: false)
workspaces.data.each do |workspace|
puts "#{workspace.id}: #{workspace.name}"
end
워크스페이스 보관하기:
curl -X POST "https://api.anthropic.com/v1/organizations/workspaces/wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ/archive" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01"
ant beta:organization:workspaces archive --workspace-id wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ
client = anthropic.Anthropic()
workspace = client.beta.organization.workspaces.archive(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
)
print(f"id: {workspace.id}")
print(f"archived_at: {workspace.archived_at}")
const client = new Anthropic();
const workspace = await client.beta.organization.workspaces.archive(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
);
console.log(`id: ${workspace.id}`);
console.log(`archived_at: ${workspace.archived_at}`);
AnthropicClient client = new();
var workspace = await client.Beta.Organization.Workspaces.Archive(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
);
Console.WriteLine($"id: {workspace.ID}");
Console.WriteLine($"archived_at: {workspace.ArchivedAt:O}");
client := anthropic.NewClient()
workspace, err := client.Beta.Organization.Workspaces.Archive(context.Background(), "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ")
if err != nil {
log.Fatal(err)
}
fmt.Printf("id: %s\n", workspace.ID)
fmt.Printf("archived_at: %s\n", workspace.ArchivedAt)
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
var workspace = client.beta().organization().workspaces()
.archive("wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ");
IO.println("id: " + workspace.id());
IO.println("archived_at: " + workspace.archivedAt().orElseThrow());
$client = new Client();
$workspace = $client->beta->organization->workspaces->archive(
workspaceID: 'wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ',
);
echo "id: {$workspace->id}\n";
echo "archived_at: {$workspace->archivedAt?->format(DATE_ATOM)}\n";
client = Anthropic::Client.new
workspace_id = "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
workspace = client.beta.organization.workspaces.archive(workspace_id)
puts "id: #{workspace.id}"
puts "archived_at: #{workspace.archived_at}"
전체 파라미터 세부사항과 응답 스키마는 Workspaces API reference를 참고하세요.
워크스페이스 구성원 관리
워크스페이스에 구성원 추가:
curl -X POST "https://api.anthropic.com/v1/organizations/workspaces/wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ/members" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"user_id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"workspace_role": "workspace_developer"
}'
ant beta:organization:workspaces:members add \
--workspace-id wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ \
--user-id user_01XyDMpzjS89pFZXqSFUBDr6 \
--workspace-role workspace_developer
client = anthropic.Anthropic()
member = client.beta.organization.workspaces.members.add(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
user_id="user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_role="workspace_developer",
)
print(f"user_id: {member.user_id}")
print(f"workspace_role: {member.workspace_role}")
const client = new Anthropic();
const member = await client.beta.organization.workspaces.members.add(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
{
user_id: "user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_role: "workspace_developer"
}
);
console.log(`user_id: ${member.user_id}`);
console.log(`workspace_role: ${member.workspace_role}`);
using Anthropic.Models.Beta.Organization.Workspaces;
AnthropicClient client = new();
var member = await client.Beta.Organization.Workspaces.Members.Add(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
new()
{
UserID = "user_01XyDMpzjS89pFZXqSFUBDr6",
WorkspaceRole = BetaNoBillingWorkspaceRole.WorkspaceDeveloper
}
);
Console.WriteLine($"user_id: {member.UserID}");
Console.WriteLine($"workspace_role: {member.WorkspaceRole.Raw()}");
client := anthropic.NewClient()
member, err := client.Beta.Organization.Workspaces.Members.Add(
context.Background(),
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
anthropic.BetaOrganizationWorkspaceMemberAddParams{
UserID: "user_01XyDMpzjS89pFZXqSFUBDr6",
WorkspaceRole: anthropic.BetaNoBillingWorkspaceRoleWorkspaceDeveloper,
},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("user_id: %s\n", member.UserID)
fmt.Printf("workspace_role: %s\n", member.WorkspaceRole)
import com.anthropic.models.beta.organization.workspaces.BetaNoBillingWorkspaceRole;
import com.anthropic.models.beta.organization.workspaces.members.MemberAddParams;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
var params = MemberAddParams.builder()
.userId("user_01XyDMpzjS89pFZXqSFUBDr6")
.workspaceRole(BetaNoBillingWorkspaceRole.WORKSPACE_DEVELOPER)
.build();
var member = client.beta().organization().workspaces().members()
.add("wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ", params);
IO.println("user_id: " + member.userId());
IO.println("workspace_role: " + member.workspaceRole().asString());
}
use Anthropic\Beta\Organization\Workspaces\NoBillingWorkspaceRole;
// ...
$client = new Client();
$member = $client->beta->organization->workspaces->members->add(
workspaceID: 'wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ',
userID: 'user_01XyDMpzjS89pFZXqSFUBDr6',
workspaceRole: NoBillingWorkspaceRole::WORKSPACE_DEVELOPER,
);
echo "user_id: {$member->userID}\n";
echo "workspace_role: {$member->workspaceRole}\n";
client = Anthropic::Client.new
workspace_id = "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
member = client.beta.organization.workspaces.members.add(
workspace_id,
user_id: "user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_role: :workspace_developer
)
puts "user_id: #{member.user_id}"
puts "workspace_role: #{member.workspace_role}"
구성원의 역할 업데이트:
curl -X POST "https://api.anthropic.com/v1/organizations/workspaces/wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ/members/user_01XyDMpzjS89pFZXqSFUBDr6" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"workspace_role": "workspace_admin"}'
ant beta:organization:workspaces:members update \
--user-id user_01XyDMpzjS89pFZXqSFUBDr6 \
--workspace-id wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ \
--workspace-role workspace_admin
client = anthropic.Anthropic()
member = client.beta.organization.workspaces.members.update(
"user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
workspace_role="workspace_admin",
)
print(f"user_id: {member.user_id}")
print(f"workspace_role: {member.workspace_role}")
const client = new Anthropic();
const member = await client.beta.organization.workspaces.members.update(
"user_01XyDMpzjS89pFZXqSFUBDr6",
{
workspace_id: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
workspace_role: "workspace_admin"
}
);
console.log(`user_id: ${member.user_id}`);
console.log(`workspace_role: ${member.workspace_role}`);
using Anthropic.Models.Beta.Organization.Workspaces;
AnthropicClient client = new();
var member = await client.Beta.Organization.Workspaces.Members.Update(
"user_01XyDMpzjS89pFZXqSFUBDr6",
new()
{
WorkspaceID = "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
WorkspaceRole = BetaWorkspaceRole.WorkspaceAdmin
}
);
Console.WriteLine($"user_id: {member.UserID}");
Console.WriteLine($"workspace_role: {member.WorkspaceRole.Raw()}");
client := anthropic.NewClient()
member, err := client.Beta.Organization.Workspaces.Members.Update(
context.Background(),
"user_01XyDMpzjS89pFZXqSFUBDr6",
anthropic.BetaOrganizationWorkspaceMemberUpdateParams{
WorkspaceID: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
WorkspaceRole: anthropic.BetaWorkspaceRoleWorkspaceAdmin,
},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("user_id: %s\n", member.UserID)
fmt.Printf("workspace_role: %s\n", member.WorkspaceRole)
import com.anthropic.models.beta.organization.workspaces.BetaWorkspaceRole;
import com.anthropic.models.beta.organization.workspaces.members.MemberUpdateParams;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
var params = MemberUpdateParams.builder()
.workspaceId("wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ")
.workspaceRole(BetaWorkspaceRole.WORKSPACE_ADMIN)
.build();
var member = client.beta().organization().workspaces().members()
.update("user_01XyDMpzjS89pFZXqSFUBDr6", params);
IO.println("user_id: " + member.userId());
IO.println("workspace_role: " + member.workspaceRole().asString());
}
use Anthropic\Beta\Organization\Workspaces\WorkspaceRole;
// ...
$client = new Client();
$member = $client->beta->organization->workspaces->members->update(
userID: 'user_01XyDMpzjS89pFZXqSFUBDr6',
workspaceID: 'wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ',
workspaceRole: WorkspaceRole::WORKSPACE_ADMIN,
);
echo "user_id: {$member->userID}\n";
echo "workspace_role: {$member->workspaceRole}\n";
client = Anthropic::Client.new
user_id = "user_01XyDMpzjS89pFZXqSFUBDr6"
member = client.beta.organization.workspaces.members.update(
user_id,
workspace_id: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
workspace_role: :workspace_admin
)
puts "user_id: #{member.user_id}"
puts "workspace_role: #{member.workspace_role}"
워크스페이스에서 구성원 제거:
curl -X DELETE "https://api.anthropic.com/v1/organizations/workspaces/wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ/members/user_01XyDMpzjS89pFZXqSFUBDr6" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01"
ant beta:organization:workspaces:members remove \
--user-id user_01XyDMpzjS89pFZXqSFUBDr6 \
--workspace-id wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ
client = anthropic.Anthropic()
removed_member = client.beta.organization.workspaces.members.remove(
"user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
)
print(f"user_id: {removed_member.user_id}")
const client = new Anthropic();
const removedMember = await client.beta.organization.workspaces.members.remove(
"user_01XyDMpzjS89pFZXqSFUBDr6",
{ workspace_id: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ" }
);
console.log(`user_id: ${removedMember.user_id}`);
AnthropicClient client = new();
var removedMember = await client.Beta.Organization.Workspaces.Members.Remove(
"user_01XyDMpzjS89pFZXqSFUBDr6",
new() { WorkspaceID = "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ" }
);
Console.WriteLine($"user_id: {removedMember.UserID}");
client := anthropic.NewClient()
removedMember, err := client.Beta.Organization.Workspaces.Members.Remove(
context.Background(),
"user_01XyDMpzjS89pFZXqSFUBDr6",
anthropic.BetaOrganizationWorkspaceMemberRemoveParams{
WorkspaceID: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("user_id: %s\n", removedMember.UserID)
import com.anthropic.models.beta.organization.workspaces.members.MemberRemoveParams;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
var params = MemberRemoveParams.builder()
.workspaceId("wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ")
.build();
var removedMember = client.beta().organization().workspaces().members()
.remove("user_01XyDMpzjS89pFZXqSFUBDr6", params);
IO.println("user_id: " + removedMember.userId());
}
$client = new Client();
$removedMember = $client->beta->organization->workspaces->members->remove(
userID: 'user_01XyDMpzjS89pFZXqSFUBDr6',
workspaceID: 'wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ',
);
echo "user_id: {$removedMember->userID}\n";
client = Anthropic::Client.new
user_id = "user_01XyDMpzjS89pFZXqSFUBDr6"
removed_member = client.beta.organization.workspaces.members.remove(
user_id,
workspace_id: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
)
puts "user_id: #{removed_member.user_id}"
전체 파라미터 세부사항은 Workspace Members API reference를 참고하세요.
API 키와 리소스 범위
모든 요청은 정확히 하나의 워크스페이스에서 실행되며 그 워크스페이스 안의 리소스에만 접근할 수 있어요. 어떤 워크스페이스인지는 키 타입에 따라 달라져요:
- 워크스페이스 키(소유자가 없는 레거시 키)는 만들어진 워크스페이스에 속하며 항상 거기서 실행돼요.
- 개인 키나 서비스 계정 키는 그 사용자나 서비스 계정으로 행동해요. 단일 워크스페이스 키는 만들 때 선택된 워크스페이스에서 항상 실행돼요. 다중 워크스페이스 키는 각 요청의
anthropic-workspace-id헤더가 명명한 워크스페이스에서 실행돼요. 계정은 그것을 사용하려면 워크스페이스에 접근이 있어야 해요.
워크스페이스에 범위가 제한된 리소스는:
- Files API를 통해 만든 Files(파일)
- Batch API를 통해 만든 Message Batches
- Skills API를 통해 만든 Skills(스킬)
일부 리소스는 다르게 관리돼요:
- MCP 터널 은 API 키가 아니라 Workload Identity Federation을 통해 얻은
workspace:manage_tunnelsOAuth 토큰으로 관리돼요. 터널은 워크스페이스에서 만들어지고, Console MCP tunnels 목록과 Managed Agent 서버 선택기는 현재 워크스페이스의 터널만 보여줘요. 활성 터널 10개의 상한은 조직 전체에 적용돼요. 터널 관리는 터널 관리 권한이 있는 역할이 필요해요. 조직 개발자는 볼 수는 있지만 변경할 수는 없어요. - 워크스페이스 자체와 조직 구성원은 Admin API 키,
org:adminOAuth 토큰, 또는 특정 워크스페이스에 범위가 제한되지 않은 개인·서비스 계정 키를 사용하는 Admin API를 통해 조직 수준에서 관리돼요.
조직의 워크스페이스 ID를 조회하려면 List Workspaces 엔드포인트를 호출하거나 Claude Console에서 찾으세요.
참고 프롬프트 캐시도 Claude API, AWS의 Claude Platform, Microsoft Foundry에서 워크스페이스별로 격리돼요. Amazon Bedrock과 Google Cloud에서는 프롬프트 캐시가 조직별로 격리돼요.
API 응답 뒤의 워크스페이스 식별
Claude API 응답은 request-id와 anthropic-organization-id 응답 헤더와 함께 anthropic-workspace-id 헤더를 포함해요. 그 값은 요청의 API 키나 접근 토큰이 해석된 워크스페이스의 wrkspc_ 접두사 ID이며, 그 워크스페이스가 기본 워크스페이스여도 포함돼요. 예를 들어 성공한 응답은 이런 헤더를 포함해요:
HTTP/1.1 200 OK
request-id: req_018EeWyXxfu5pfWkrYcMdjWG
anthropic-organization-id: 0d0e7a3b-52f1-4c7e-9a51-3f6f2f7c1b9e
anthropic-workspace-id: wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ
자격 증명이 워크스페이스로 해석되지 않을 때(예: Admin API 요청) 또는 401 오류처럼 인증이 완료되기 전에 요청이 실패할 때는 헤더가 없어요.
다음 예시들은 Messages API 요청을 보내고 응답 헤더에서 워크스페이스 ID를 출력해요:
# -D - prints the response headers; -o /dev/null discards the body
curl -sS -D - -o /dev/null https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello, Claude"}]
}' | grep -i '^anthropic-workspace-id'
# --debug prints the HTTP response, including the Anthropic-Workspace-Id
# header, to stderr; > /dev/null hides the JSON body on stdout
ant --debug messages create \
--model claude-opus-5-5 \
--max-tokens 1024 \
--message '{role: user, content: "Hello, Claude"}' > /dev/null
client = anthropic.Anthropic()
response = client.messages.with_raw_response.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
workspace_id = response.headers.get("anthropic-workspace-id")
print(f"Workspace ID: {workspace_id}")
const client = new Anthropic();
const { response } = await client.messages
.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }]
})
.withResponse();
console.log("Workspace ID:", response.headers.get("anthropic-workspace-id"));
AnthropicClient client = new();
using var response = await client.WithRawResponse.Messages.Create(new()
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages = [new() { Role = Role.User, Content = "Hello, Claude" }]
});
var workspaceId = response.GetHeaderValues("anthropic-workspace-id").First();
Console.WriteLine($"Workspace ID: {workspaceId}");
client := anthropic.NewClient()
var response *http.Response
_, err := client.Messages.New(
context.Background(),
anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
},
},
option.WithResponseInto(&response),
)
if err != nil {
log.Fatal(err)
}
fmt.Println("Workspace ID:", response.Header.Get("anthropic-workspace-id"))
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.http.HttpResponseFor;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
HttpResponseFor<Message> response = client.messages().withRawResponse().create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addUserMessage("Hello, Claude")
.build()
);
String workspaceId = response.headers().values("anthropic-workspace-id").getFirst();
IO.println("Workspace ID: " + workspaceId);
}
$client = new Client();
$response = $client->messages->raw->create([
'model' => Model::CLAUDE_OPUS_5_5,
'maxTokens' => 1024,
'messages' => [['role' => 'user', 'content' => 'Hello, Claude']],
]);
echo 'Workspace ID: ' . $response->getHeaderLine('anthropic-workspace-id') . "\n";
client = Anthropic::Client.new
# Read response headers in per-request middleware, which receives the
# raw HTTP response before the SDK parses it
workspace_id = nil
read_workspace_id = lambda do |request, call_next|
response = call_next.call(request)
# Keys in response.headers are lowercase
workspace_id = response.headers["anthropic-workspace-id"]
response
end
client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
request_options: { middleware: [read_workspace_id] }
)
puts "Workspace ID: #{workspace_id}"
Workspace ID: wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ
같은 접근자들은 Claude Managed Agents API를 포함한 다른 Claude API 엔드포인트에서도 헤더를 읽어요. 예를 들어 세션을 만드는 응답에서 anthropic-workspace-id를 읽어 세션이 어떤 워크스페이스에 속하는지 기록하세요.
응답의 워크스페이스 ID로 다음을 할 수 있어요:
- 요청이 어떤 워크스페이스의 사용량·비용·속도 제한에 집계됐는지 확인.
- Usage and Cost API 보고서와 API 키 같은 Admin API 객체의
workspace_id필드와 대조하기(둘 다 기본 워크스페이스와 모든 워크스페이스 키에는null을 보고합니다. API 키의scope필드가 둘을 구분하고, 한 워크스페이스에 바인딩된 키에는 그 워크스페이스의 실제 ID를 나른다). - Get Workspace에 Admin API 키로 전달해 내 기본 워크스페이스 ID인지 확인. 기본 워크스페이스는 List Workspaces가 생략하지만
"name": "Default"와 함께 돌아온다. - Console에서 그 워크스페이스를 열어 세션, 파일, 메시지 배치, 스킬 같은 요청의 리소스를 찾기.
워크스페이스 제한
각 워크스페이스에 커스텀 지출·속도 제한을 설정해 과다 사용을 막고 공정한 리소스 분배를 보장할 수 있어요.
워크스페이스 제한 설정
워크스페이스 제한은 조직 제한보다 낮게(높게는 불가) 설정할 수 있어요:
- 지출 한도(Spend limits): 워크스페이스의 월 지출을 상한으로 정하세요. Claude Console의 워크스페이스 Spend limits 설정 탭에서 설정하세요.
- 속도 제한(Rate limits): 분당 요청, 분당 입력 토큰, 분당 출력 토큰을 제한하세요. Claude Console의 워크스페이스 Rate limits 설정 탭에서 설정하세요.
참고
- 기본 워크스페이스에는 제한을 설정할 수 없어요.
- 설정하지 않으면 워크스페이스 제한은 조직 제한과 같아요.
- 조직 전체 제한은 워크스페이스 제한이 합쳐 더 커도 항상 적용돼요.
속도 제한과 작동 방식에 대한 자세한 정보는 Rate limits를 참고하세요. Rate Limits API로 현재 조직·워크스페이스 속도 제한을 프로그래밍 방식으로 읽을 수도 있어요.
사용량과 비용 추적
Usage and Cost API로 워크스페이스별 사용량과 비용을 추적하세요:
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-01T00:00:00Z&\
ending_at=2025-01-08T00:00:00Z&\
workspace_ids[]=wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ&\
group_by[]=workspace_id&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_API_KEY"
기본 워크스페이스에 귀속된 사용량과 비용은 workspace_id 값이 null이에요.
흔한 사용 사례
환경 분리
개발, 스테이징, 프로덕션용 별도 워크스페이스를 만드세요:
| 워크스페이스 | 목적 |
|---|---|
| Development | 더 낮은 속도 제한으로 테스트·실험 |
| Staging | 프로덕션과 같은 제한으로 프로덕션 전 테스트 |
| Production | 전체 속도 제한과 모니터링으로 실트래픽 |
팀 또는 부서 격리
비용 할당과 접근 제어를 위해 워크스페이스를 다른 팀에 할당하세요:
- 개발자 접근이 있는 엔지니어링 팀
- 자체 API 키가 있는 데이터 사이언스 팀
- 고객 도구를 위한 제한된 접근이 있는 지원 팀
프로젝트 기반 조직
특정 프로젝트나 제품용 워크스페이스를 만들어 사용량과 비용을 별도로 추적하세요.
모범 사례
- 워크스페이스 구조 계획 만들기 전에 워크스페이스를 어떻게 조직할지 고려하세요. 청구, 접근 제어, 사용량 추적 요구를 생각하세요.
- 의미 있는 이름 사용 목적을 나타내도록 워크스페이스를 명확히 명명하세요(예: "Production - Customer Chatbot" 또는 "Dev - Internal Tools").
- 적절한 제한 설정 예상치 못한 비용을 막고 공정한 리소스 분배를 위해 지출·속도 제한을 구성하세요.
- 정기적으로 접근 감사 적절한 사용자만 접근하는지 확인하려고 워크스페이스 구성원 자격을 주기적으로 검토하세요.
- 사용량 모니터링 Usage and Cost API로 워크스페이스 수준 소비를 추적하세요.
FAQ
기본 워크스페이스가 뭔가요?
모든 조직에는 이름을 바꾸거나 보관하거나 삭제할 수 없는 "기본 워크스페이스"가 있어요. 모든 워크스페이스처럼 wrkspc_ ID를 가진다. API는 그것을 anthropic-workspace-id 응답 헤더에서 반환하고, Get Workspace와 Update Workspace에 전달할 수 있어요. 자체 구성원 목록이 없는데, 접근이 각 구성원의 조직 역할을 따르기 때문이에요. List Workspaces 결과에는 나타나지 않고, 그것에 속한 API 키·사용량 보고·비용 보고는 모든 워크스페이스 키처럼 workspace_id에 null을 보여줘요. API 키의 scope 필드가 둘을 구분하고, 기본 워크스페이스에 속한 키에는 실제 ID를 나른다.
Claude Code 워크스페이스가 뭔가요?
Anthropic은 조직 구성원이 처음 Console 계정으로 Claude Code에 로그인할 때 Claude Code 워크스페이스를 자동으로 만들어요. Claude Code의 API 키, 사용량, 속도 제한을 다른 워크로드에서 격리해요. 자세한 내용은 Claude Code 워크스페이스를 참고하세요.
워크스페이스에 제한이 있나요?
그래요. 각 조직은 기본적으로 최대 100개 워크스페이스를 가질 수 있고, 보관된 워크스페이스는 이 제한에 집계되지 않아요. 더 필요하면 계정 팀에 연락하세요.
조직 역할은 워크스페이스 접근에 어떤 영향을 주나요?
조직 관리자는 모든 워크스페이스에서 자동으로 Workspace Admin 역할을 받아요. 조직 청구 구성원은 자동으로 Workspace Billing 역할을 받아요. 조직 사용자와 개발자는 각 워크스페이스에 수동으로 추가되어야 해요.
워크스페이스에서 어떤 역할을 할당할 수 있나요?
조직 사용자와 개발자는 Workspace Admin, Workspace Developer, Workspace Limited Developer, Workspace User 역할을 할당받을 수 있어요. Workspace Billing 역할은 수동으로 할당할 수 없으며, 조직 billing 역할을 가짐으로써 상속돼요.
조직 관리자나 청구 구성원의 워크스페이스 역할을 바꿀 수 있나요?
조직 관리자와 청구 구성원은 그 조직 역할을 보유하는 동안 워크스페이스 역할을 바꾸거나 워크스페이스에서 제거할 수 없어요(예외 하나: 청구 구성원은 Workspace Admin 역할로 승격될 수 있음). 이 제약을 받는 다른 모든 사람은 워크스페이스 접근을 바꾸려면 먼저 조직 역할을 바꾸세요.
조직 역할이 바뀌면 워크스페이스 접근은 어떻게 되나요?
조직 관리자나 청구 구성원이 사용자나 개발자로 강등되면, 수동으로 역할을 할당받은 워크스페이스를 제외한 모든 워크스페이스 접근을 잃어요. 사용자가 관리자나 청구 역할로 승격되면 모든 워크스페이스에 자동 접근을 얻어요.
사용자가 워크스페이스에서 제거되면 API 키는 어떻게 되나요?
동작은 키 타입에 따라 달라져요.
개인 키나 서비스 계정 키는 그 사용자나 서비스 계정이 워크스페이스에서 제거된 직후 그 워크스페이스에서 작동을 멈춰요. 서비스 계정 키는 그것을 만든 사용자가 제거돼도 계속 작동해요. 워크스페이스 API 키는 계속 작동해요. Claude Code 워크스페이스에서 각 키는 만든 구성원에 바인딩되고, 그 구성원이 제거되면 작동을 멈춰요.
개인 키는 소유자가 조직에서 제거되면 보관돼요. 사용자가 재초대되면 새 키를 만들어야 하고, 보관된 키는 복원되지 않아요.