AgentOffice External API 명세서
외부 시스템에서 API key로 회사의 회사 멤버 / 목표(태스크) / 지식베이스를 관리하는 REST API.
마지막 갱신: 2026-05-05
목차
기본 정보
| 항목 | 값 |
|---|---|
| Base URL | https://axtodo.com/api/v1/external |
| 요청 형식 | application/json (UTF-8) |
| 응답 형식 | application/json. 모든 응답은 { "data": ... } 또는 { "error": "...", "code": "..." } |
| 타임존 | 모든 datetime은 ISO 8601 UTC (예: 2026-05-05T08:30:00.000Z) |
| ID 타입 | string. 내부적으로는 BigInt이지만 number 직렬화가 발생 — number/string 양쪽 모두 받음 |
인증
모든 엔드포인트는 API key 인증 필요. 두 가지 헤더 방식 모두 지원:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
또는
X-API-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
API key 발급
- 대시보드 로그인 → 좌측 사이드바 "설정" 클릭
- 페이지 하단 "API Keys" 섹션 → "+ Generate Key"
- 이름 + 만료 기간 선택 (30일 / 90일 / 1년 / 만료 없음 / 직접 지정 — 기본 90일)
- 키는 한 번만 표시 — 즉시 안전한 곳에 저장
- 분실 시 폐기 후 재발급
인증 실패
| 상태 | code | 의미 |
|---|---|---|
| 401 | MISSING_API_KEY | 헤더 없음 |
| 401 | INVALID_API_KEY | 키가 존재하지 않음 또는 형식 오류 |
| 401 | KEY_REVOKED | 폐기된 키 |
| 401 | KEY_EXPIRED | 만료된 키 |
공통 응답 형식
성공
{
"data": { /* 단일 객체 */ }
}
{
"data": [ /* 배열 */ ]
}
에러
{
"error": "사람이 읽을 수 있는 메시지",
"code": "MACHINE_CODE",
"issues": [ /* 검증 실패 시 zod issues */ ]
}
상태 코드
| 코드 | 의미 |
|---|---|
| 200 | 성공 |
| 201 | 생성 성공 (POST) |
| 400 | 입력 검증 실패 (VALIDATION_ERROR) |
| 401 | 인증 실패 |
| 403 | 권한 없음 (현재 미구현 — 향후) |
| 404 | 리소스 없음 (NOT_FOUND) |
| 422 | 도메인 검증 실패 (예: invalid type) |
| 500 | 서버 에러 (INTERNAL_ERROR) |
에이전트 API (read-only)
회사에 배치된 AI 에이전트 조회. 읽기 전용 — 생성/수정/삭제는 대시보드 또는 internal API에서만 (LLM 안전성 보장).
GET /companies/:slug/agents
배포된 에이전트 목록 (소프트 삭제 제외).
Response 200:
{
"data": [
{
"id": "1",
"name": "김서연",
"roleTitle": "프론트엔드 개발자",
"roleGoal": "...",
"tier": "mid",
"power": "on",
"currentState": "working",
"mode": "independent",
"hiredType": "cloned",
"llmProvider": "claude",
"llmModel": "claude-opus-4-7",
"createdAt": "2026-04-01T00:00:00.000Z"
}
]
}
필드 enum:
tier:junior|mid|senior|leadpower:on|offcurrentState:idle|thinking|working|collaborating|waiting|done|sleeping|errormode:independent|collaborativehiredType:cloned|subscribed|outsourced|rookiellmProvider:claude|openai|google
예시:
curl -H "Authorization: Bearer sk_live_xxx" \
https://axtodo.com/api/v1/external/companies/team-8/agents
GET /companies/:slug/agents/:id
단일 에이전트 상세 (assignedDesk, skills, knowledge, growthStats, proficiencies 포함).
Response 200: { data: AgentItem } — 위 필드 + 관계 데이터
Response 404: { error: "Agent not found", code: "NOT_FOUND" }
노출되지 않는 작업
다음 작업은 외부 API에 노출되지 않음. 대시보드 또는 internal JWT API 사용:
| 작업 | 위치 |
|---|---|
| 에이전트 생성 (마켓 고용) | POST /api/companies/:slug/agents (JWT) 또는 대시보드 마켓 |
| instructions / rules / skills / knowledge 편집 | /api/companies/:slug/agents/:id/soul/* (JWT) 또는 대시보드 → 에이전트 → Soul 탭 |
| power on/off | POST /api/companies/:slug/agents/:id/power (JWT) 또는 대시보드 |
| 삭제 | DELETE /api/companies/:slug/agents/:id (JWT) |
LLM 자체에 영향을 주는 변경은 클라이언트 책임 한계 안에서만 허용됩니다. 외부 통합에서 자동 변경이 필요하면 향후 PR로 별도 scope-제한 키 발급 검토.
회사 멤버 API
회사에 속한 사람 멤버 (에이전트는 별도 — 현재 외부 API 미노출).
GET /companies/:slug/members
회사 멤버 목록 조회.
Path params:
slug(string) — 회사 슬러그 (예:team-8)
Response 200:
{
"data": [
{
"id": "1",
"companyId": "8",
"userId": "42",
"role": "owner",
"note": null,
"user": {
"id": "42",
"email": "user@example.com",
"name": "홍길동",
"department": null,
"position": null
},
"createdAt": "2026-04-01T00:00:00.000Z"
}
]
}
role enum: owner | vice_owner | member
예시:
curl -H "Authorization: Bearer sk_live_xxx" \
https://axtodo.com/api/v1/external/companies/team-8/members
현재 외부 API에서는 조회만 지원. 초대/역할 변경/제거는 대시보드 UI에서.
목표(태스크) API
회사 단위 태스크. taskType으로 에이전트 성장(agent_task) vs 프로젝트 목표(team_todo) 구분. parentTaskId로 트리 구조 (만다라트 슬롯 = orderIndex 0..7).
GET /companies/:slug/tasks
태스크 목록 (최대 50개, createdAt desc).
Query:
status(optional) —pending|assigned|in_progress|done|failedagentId(optional) — 특정 에이전트 담당 태스크만
Response 200: { data: TaskItem[] }
interface TaskItem {
id: string;
title: string;
description: string;
status: 'pending' | 'assigned' | 'in_progress' | 'done' | 'failed';
priority: 'low' | 'medium' | 'high' | 'urgent';
taskType: 'agent_task' | 'team_todo';
parentTaskId: string | null;
assignedAgentId: string | null;
result: string | null;
tokensUsed: number;
dueDate: string | null;
startDate: string | null;
startedAt: string | null;
completedAt: string | null;
weight: number | null;
depth: number;
orderIndex: number;
createdAt: string;
updatedAt: string;
}
GET /companies/:slug/tasks/:id
단일 태스크 상세. 404 NOT_FOUND 가능.
POST /companies/:slug/tasks
태스크 생성.
Body:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
title | string (1..200) | ✅ | 제목 |
description | string | ❌ | 비우면 title로 fallback |
priority | enum | ❌ | 기본 medium |
taskType | enum | ❌ | 기본 team_todo |
assignedAgentId | string | number | ❌ | 담당 에이전트. 지정 시 status=assigned |
parentTaskId | string | number | ❌ | 만다라트 부모 |
dueDate | ISO 8601 | ❌ | 마감일 |
Response 201: { data: TaskItem }
예시 — 만다라트 sub-task 생성:
curl -X POST https://axtodo.com/api/v1/external/companies/team-8/tasks \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Q2 매출 분석",
"description": "지난 분기 대비 매출 증감 보고서",
"priority": "high",
"taskType": "team_todo",
"parentTaskId": "123",
"dueDate": "2026-06-30T23:59:59Z"
}'
PATCH /companies/:slug/tasks/:id
태스크 부분 갱신. 모든 필드 optional.
Body (모두 optional):
| 필드 | 타입 |
|---|---|
title | string (1..200) |
description | string |
priority | enum |
status | enum (pending/assigned/in_progress/done/failed) |
dueDate | ISO 8601 | null (null = 마감일 제거) |
assignedAgentId | string | number | null (null = 담당 해제) |
Response 200: { data: TaskItem }
주의: 외부 API의 status 변경은 internal
task.service의 자동 hook(startedAt/completedAt 자동 설정 + TaskLog 기록)을 우회. 일관성 필요 시 status는pending→done전이 외에 internal API 권장.
DELETE /companies/:slug/tasks/:id
태스크 삭제. 자식 태스크는 parentTaskId가 null로 설정됨 (cascade 안 함).
Response 200: { data: { id: "..." } }
지식베이스 API
회사 단위 지식 항목. 에이전트별 RAG와는 분리된 t_company_knowledge 테이블.
GET /companies/:slug/knowledge
지식 목록 (최대 100개, updatedAt desc).
Query:
search(optional) — name/contentcontainsOR 검색type(optional) —post|file|markdown|url|textisActive(optional) —'true'|'false'. 기본 active만
Response 200: { data: KnowledgeItem[] }
interface KnowledgeItem {
id: string;
name: string;
type: 'post' | 'file' | 'markdown' | 'url' | 'text';
content: string; // LongText, 마크다운/URL/원본 텍스트
tags: string[];
isActive: boolean;
createdAt: string;
updatedAt: string;
}
GET /companies/:slug/knowledge/:id
단일 지식 상세.
POST /companies/:slug/knowledge
지식 생성.
Body:
| 필드 | 타입 | 필수 |
|---|---|---|
name | string (1..200) | ✅ |
type | enum | ✅ |
content | string (1..) | ✅ |
tags | string[] | ❌ |
Response 201: { data: KnowledgeItem }
예시:
curl -X POST https://axtodo.com/api/v1/external/companies/team-8/knowledge \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "코딩 컨벤션",
"type": "markdown",
"content": "# Style Guide\n\n- TypeScript strict mode\n- ...",
"tags": ["engineering", "style"]
}'
PATCH /companies/:slug/knowledge/:id
부분 갱신. name/type/content/tags/isActive 모두 optional.
Response 200: { data: KnowledgeItem }
DELETE /companies/:slug/knowledge/:id
지식 항목 삭제 (hard delete).
Response 200: { data: { id: "..." } }
파일 업로드 (R2)
type=file 지식 항목은 Cloudflare R2에 직접 업로드. 백엔드는 presigned URL만 발급, 파일 자체는 클라이언트가 R2로 PUT.
업로드 흐름 (3단계)
1) POST .../knowledge/upload-url → { uploadUrl, key }
2) PUT uploadUrl (Content-Type: <mime>, body: <file bytes>)
3) POST .../knowledge → content: <key>, type: 'file'
POST /companies/:slug/knowledge/upload-url
presigned PUT URL 발급 (10분 만료).
Body:
| 필드 | 타입 | 필수 |
|---|---|---|
filename | string (1..255) | ✅ |
contentType | string | ❌ (예: application/pdf, image/png) |
Response 200:
{
"data": {
"uploadUrl": "https://<account>.r2.cloudflarestorage.com/axtodo/team-8/...?X-Amz-Signature=...",
"key": "team-8/1714900000000-abc123-doc.pdf",
"bucket": "axtodo",
"publicUrl": "https://<account>.r2.cloudflarestorage.com/axtodo/team-8/...",
"expiresIn": 600
}
}
클라이언트 PUT (직접 R2로)
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--data-binary @./report.pdf
→ 200 (R2 응답, body 비어있음).
파일 등록
받은 key를 content로 저장:
curl -X POST https://axtodo.com/api/v1/external/companies/team-8/knowledge \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Q2 보고서",
"type": "file",
"content": "team-8/1714900000000-abc123-report.pdf",
"tags": ["report"]
}'
GET /companies/:slug/knowledge/:id/download-url
type=file 지식의 presigned GET URL 발급 (1시간 만료).
Response 200:
{
"data": {
"url": "https://<account>.r2.cloudflarestorage.com/axtodo/team-8/...?X-Amz-Signature=...",
"expiresIn": 3600,
"key": "team-8/..."
}
}
type !== 'file'이면 400 NOT_A_FILE.
풀 통합 예 (curl 3-step)
KEY="sk_live_xxx"
SLUG="team-8"
FILE="./report.pdf"
BASE="https://axtodo.com/api/v1/external"
# 1. presigned PUT 발급
RESPONSE=$(curl -s -X POST "$BASE/companies/$SLUG/knowledge/upload-url" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"filename":"report.pdf","contentType":"application/pdf"}')
UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.data.uploadUrl')
KEY_OBJ=$(echo "$RESPONSE" | jq -r '.data.key')
# 2. R2로 직접 PUT
curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @"$FILE"
# 3. 지식 등록
curl -X POST "$BASE/companies/$SLUG/knowledge" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "{\"name\":\"Q2 보고서\",\"type\":\"file\",\"content\":\"$KEY_OBJ\"}"
에러 코드
| code | HTTP | 의미 |
|---|---|---|
MISSING_API_KEY | 401 | 인증 헤더 없음 |
INVALID_API_KEY | 401 | 알 수 없는 키 |
KEY_REVOKED | 401 | 폐기됨 |
KEY_EXPIRED | 401 | 만료됨 |
VALIDATION_ERROR | 400 | 요청 body/query zod 검증 실패. 응답 issues 배열 참조 |
NOT_FOUND | 404 | 리소스 없음 |
KNOWLEDGE_NOT_FOUND | 404 | 지식 항목 없음 |
INVALID_TYPE | 422 | knowledge type enum 외 값 |
NOT_A_FILE | 400 | download-url을 type !== 'file' 항목에 호출 |
R2_ERROR | 500 | R2 storage 오류 |
INTERNAL_ERROR | 500 | 서버 오류. 운영자 문의 |
속도 제한 / 권한 한계
- 속도 제한: 현재 미적용. 향후 분당 60req 제한 예정
- 회사 스코프: API key는 발급한 사용자가 멤버인 모든 회사 접근 가능. 키 단위 회사 제한은 후속 추가 예정
- 에이전트 관리: 현재 외부 API 미노출 (대시보드 UI 또는 internal API 사용)
- 결제/구독: 외부 API 미노출
- 외부 task PATCH의 status 변경은 자동 startedAt/completedAt 갱신 hook을 우회 — 정확한 lifecycle이 필요한 통합은 internal API 권장
관련 문서
- 카탈로그:
docs/feature-catalog.md - API key 인프라:
backend/src/services/api-key.service.ts,backend/src/middleware/apiKeyAuth.ts - 외부 라우터 소스:
backend/src/api/external.ts - 도메인 용어:
.claude/rules/10-business-domain.md