워크스페이스·credential·사용자 그룹 API¶
워크스페이스는 회원, API key, 시나리오와 대화를 격리하는 테넌트 경계입니다.
이 문서에서 헤더는 X-Workspace-Id: <workspace-uuid>를 뜻합니다. 인증·테넌트
해석의 상세는 워크스페이스와 테넌시, credential 수명주기는
API key와 client-secret, 공통 타입은
스키마를 참고하십시오.
Credential 구분¶
| Credential | 발급 권한 | 소비자 | 목적 |
|---|---|---|---|
| access JWT | 로그인 성공 | App Backend | 로그인 회원 REST 인증 |
| 워크스페이스 API key | 워크스페이스 OWNER | App Backend의 허용된 GET, Socket Backend | 서버 간 읽기와 실시간 세션 인증 |
| Ed25519 client-secret | ACTIVE 멤버 | Socket Backend | 단기 실시간 세션 인증 |
API key와 client-secret은 이름이 비슷해도 서로 대체할 수 없습니다. client-secret은 App Backend REST Bearer credential이 아닙니다. API key는 App Backend의 제한된 조회와 Socket Backend 세션에 사용할 수 있지만, 브라우저에는 장기 API key 대신 client-secret을 전달해야 합니다.
워크스페이스¶
POST /v1/workspaces¶
- 인증: access JWT
- 테넌트: 부트스트랩 경로이므로 헤더 불필요
- 성공:
200 OK - 부수 효과: 워크스페이스를 생성하고 요청자를 owner이자
OWNER/ACTIVE멤버로 등록합니다. LLM provider는 서버 정책값으로 설정됩니다.
| 필드 | 필수 | 제약 |
|---|---|---|
name |
예 | 공백 불가, 최대 128자 |
sttProvider |
예 | 대소문자까지 WHISPER 또는 MAGO |
200: 생성과 OWNER 등록 성공400: 본문, 이름 또는 provider 검증 실패401: access JWT 누락·오류409: DB 유일성 충돌 등 무결성 충돌
GET /v1/workspaces¶
- 인증: access JWT
- 테넌트: 헤더 불필요
- query:
page기본 1,size기본 20 - 성공:
200 OK - 부수 효과: 없음
요청자의 ACTIVE 멤버십이 있는 워크스페이스만 최신 생성순으로 조회합니다.
{
"status": 200,
"message": "success",
"page": {
"page": 1,
"size": 20,
"totalItems": 1,
"totalPages": 1,
"hasNext": false,
"hasPrevious": false,
"first": true,
"last": true
},
"count": 1,
"list": [
{
"id": "01900000-0000-7000-8000-000000000001",
"workspaceName": "우리 병원",
"createdAt": "2026-09-23T10:00:00"
}
]
}
200: 목록 또는 빈 목록401: 인증 오류
초대, 역할 변경, 소유권 이전 API는 기준 커밋에 없습니다.
워크스페이스 API key¶
세 경로 모두 access JWT, 헤더, 해당 워크스페이스의 ACTIVE 멤버십과 OWNER 역할이
필요합니다. API key 자체로 API key를 관리할 수 없습니다.
POST /v1/workspaces/api-keys¶
- 성공:
200 OK - 부수 효과: 랜덤 원본 키를 생성하고 DB에는 SHA-256 해시와 표시용 접두만 저장
| 필드 | 필수 | 제약 |
|---|---|---|
name |
예 | 공백 불가, 최대 128자 |
{
"status": 200,
"message": "success",
"data": {
"id": "01900000-0000-7000-8000-000000000101",
"name": "분석 조회 서버",
"key": "<new-workspace-api-key>",
"keyPrefix": "<display-prefix>",
"keyType": "TEST",
"createdAt": "2026-09-23T10:05:00"
}
}
key는 이 응답에서만 한 번 확인할 수 있습니다. 예시는 실제 접두나 키 값을 제시하지
않으며, 운영·개발 프로파일에 따라 keyType과 접두 정책이 달라질 수 있습니다.
200: 발급 성공400: 헤더 누락·형식 오류, 본문 또는 이름 검증 실패401: access JWT 오류403: 비멤버 또는 OWNER 아님404: 워크스페이스 없음
GET /v1/workspaces/api-keys¶
- 요청 query·본문: 없음
- 성공:
200 OK - 부수 효과: 없음
{
"status": 200,
"message": "success",
"data": [
{
"id": "01900000-0000-7000-8000-000000000101",
"keyName": "분석 조회 서버",
"keyPrefix": "<display-prefix>",
"keyType": "TEST",
"keyStatus": "ACTIVE",
"lastUsedAt": null,
"createdAt": "2026-09-23T10:05:00"
}
]
}
원본 키와 secretHash는 반환하지 않습니다. 폐기된 키도 감사 목적으로 남으며 상태는
ACTIVE, REVOKED, ROTATED 중 저장된 값을 그대로 보여 줍니다.
200: 목록 반환400: 헤더 누락·형식 오류401: 인증 오류403: 비멤버 또는 OWNER 아님404: 워크스페이스 없음
DELETE /v1/workspaces/api-keys/{keyId}¶
- path
keyId: API key 레코드 UUID - 성공:
200 OK - 부수 효과: 물리 삭제하지 않고
keyStatus=REVOKED로 전환. 재호출은 멱등 성공
200: 폐기 완료 또는 이미 폐기됨400: 헤더나 UUID 형식 오류401: 인증 오류403: 비멤버 또는 OWNER 아님404: 워크스페이스가 없거나keyId가 대상 워크스페이스에 속하지 않음
API key 사용 시에는 다음처럼 전달합니다.
허용 범위는 SCOPE_WORKSPACE_READ이며 회원 access JWT 역할을 대신하지 않습니다.
Ed25519 client-secret¶
POST /v1/workspaces/client-secrets¶
- 인증: access JWT만 사용
- 테넌트: 헤더 필수
- 권한: 해당 워크스페이스의 ACTIVE 멤버; OWNER일 필요 없음
- 본문: 전체 생략 가능
- 성공:
201 Created - 부수 효과: Ed25519 개인키로 단기 JWT를 서명. DB 저장,
jti소비 기록, Socket Backend 호출은 하지 않음
| 필드 | 필수 | 제약·동작 |
|---|---|---|
expiresInSeconds |
아니요 | 정수. 생략 시 서버 설정값, 300 미만은 300, 10,800 초과는 10,800으로 clamp |
{
"status": 200,
"message": "success",
"data": {
"clientSecret": {
"value": "<ed25519-client-secret-jwt>",
"expiresInSeconds": 1800,
"expiresAt": "2026-09-23T01:30:00Z"
}
}
}
201: 발급 성공. body의 공통status는 200400: 헤더 누락·형식 오류 또는 역직렬화할 수 없는 본문401: access JWT 오류403: 대상 워크스페이스의 ACTIVE 멤버가 아님
토큰 header는 alg=EdDSA, typ=JWT이고 claim은 iss, 단일 문자열 aud, sub,
workspaceId, jti, iat, exp입니다. apiKeyId는 없습니다. 상세 값과 의미는
스키마에 있습니다.
응답 1회 전달과 1회 사용은 다릅니다
client-secret은 응답에서만 전달되고 서버에 저장되지 않지만 일회용 토큰은 아닙니다. 유효 기간 안에는 재사용할 수 있습니다. access JWT의 HMAC 키와 분리된 Ed25519 서명키를 사용하며, Socket Backend는 공개키로 검증합니다.
사용자 그룹¶
모든 사용자 그룹 경로는 access JWT, 헤더, 대상 워크스페이스의 ACTIVE OWNER가
필요합니다. API key로 관리할 수 없습니다.
POST /v1/end-user-groups¶
- 성공:
201 Created - 부수 효과: 워크스페이스 범위 사용자 그룹 생성
| 필드 | 필수 | 제약 |
|---|---|---|
name |
예 | 공백 불가, trim 후 최대 128자; 제어·format·줄 구분·private-use 등 금지 문자 불가 |
{
"status": 200,
"message": "success",
"data": {
"id": "01900000-0000-7000-8000-000000000201",
"name": "2026년 상담 참여자",
"createdAt": "2026-09-23T10:10:00"
}
}
201: 생성 성공400: 헤더 또는 이름 검증 실패401: 인증 오류403: OWNER 아님409: 같은 워크스페이스의 활성 그룹 이름 중복
GET /v1/end-user-groups¶
- 요청 query·본문: 없음
- 성공:
200 OK - 부수 효과: 없음
{
"status": 200,
"message": "success",
"data": [
{
"id": "01900000-0000-7000-8000-000000000201",
"name": "2026년 상담 참여자",
"createdAt": "2026-09-23T10:10:00"
}
]
}
200: 목록 또는 빈 배열400/401/403: 헤더, 인증 또는 OWNER 검증 실패
POST /v1/end-user-groups/{groupId}/members¶
- path
groupId: 현재 워크스페이스의 사용자 그룹 UUID - 성공:
200 OK - 부수 효과: 외부 사용자 ID를 그룹에 일괄 등록. 요청 내부 중복과 이미 등록된 값은 건너뜀
| 필드 | 필수 | 제약 |
|---|---|---|
externalUserIds |
예 | 비어 있지 않은 배열, 최대 1,000개 |
| 각 항목 | 예 | 공백 불가, trim 후 최대 128자, 그룹 이름과 같은 금지 문자 규칙 |
{
"status": 200,
"message": "success",
"data": {
"requestedCount": 2,
"addedCount": 1,
"existingCount": 1
}
}
200: 등록 결과400: 배열, 개수, 항목 길이·문자 검증 실패401/403: 인증 또는 OWNER 검증 실패404: 그룹이 현재 워크스페이스에 없음
GET /v1/end-user-groups/{groupId}/members¶
- path
groupId: 그룹 UUID - query:
page기본 1,size기본 20 - 정렬:
externalUserId오름차순 - 성공:
200 OK - 부수 효과: 없음
{
"status": 200,
"message": "success",
"page": {
"page": 1,
"size": 20,
"totalItems": 1,
"totalPages": 1,
"hasNext": false,
"hasPrevious": false,
"first": true,
"last": true
},
"count": 1,
"list": [
{
"id": "01900000-0000-7000-8000-000000000301",
"externalUserId": "pilot-001",
"matchedUser": true,
"createdAt": "2026-09-23T10:11:00"
}
]
}
matchedUser는 현재 workspace_end_user와 매칭되는지를 나타냅니다.
200: 페이지 반환400/401/403: query, 인증 또는 OWNER 검증 실패404: 그룹이 현재 워크스페이스에 없음
DELETE /v1/end-user-groups/{groupId}/members/{memberId}¶
groupId: 그룹 UUIDmemberId: 그룹 구성원 레코드 UUID이며externalUserId가 아님- 성공:
200 OK - 부수 효과: 그룹에서 해당 구성원을 제거
200: 제거 성공400: UUID 또는 헤더 형식 오류401/403: 인증 또는 OWNER 검증 실패404: 그룹 또는 구성원이 현재 워크스페이스·그룹에 없음