# 결재관리 API 명세

> **작성일**: 2026-03-11
> **상태**: 개발 완료 (API 배포됨)
> **Base URL**: `api.codebridge-x.com` (운영) / `api.dev.codebridge-x.com` (개발)

---

## 1. 개요

결재관리 시스템의 전체 API 명세. 기안, 결재, 참조, 위임, 완료함 등 모든 결재 워크플로우를 지원한다.

### 1.1 인증

모든 요청에 다음 헤더가 필요하다:

```
X-API-KEY: {api_key}
Authorization: Bearer {token}
```

### 1.2 공통 응답 형식

```json
{
  "success": true,
  "message": "조회되었습니다.",
  "data": { ... }
}
```

에러 시:

```json
{
  "success": false,
  "message": "에러 메시지"
}
```

---

## 2. 상태 흐름도

```
                    ┌──────────┐
                    │  draft   │ 임시저장
                    └────┬─────┘
                         │ submit (상신)
                         ▼
                    ┌──────────┐
              ┌────►│ pending  │ 결재 진행중
              │     └──┬─┬─┬──┘
              │        │ │ │
   release-   │ hold   │ │ │ approve (각 단계)
   hold       │   ▼    │ │ │
              │ ┌──────┐│ │ │
              └─┤on_hold││ │ │ 보류
                └──────┘│ │ │
                        │ │ │
        ┌───────────────┘ │ └──────────────┐
        │ cancel (회수)   │                │ reject (반려)
        ▼                 ▼                ▼
  ┌──────────┐    ┌──────────┐     ┌──────────┐
  │cancelled │    │ approved │     │ rejected │
  └──────────┘    └──────────┘     └──────────┘
                                         │
                                         │ submit (재상신)
                                         ▼
                                    ┌──────────┐
                                    │ pending  │
                                    └──────────┘
```

### 2.1 상태값

| 상태 | 값 | 설명 | UI 배지 색상 |
|------|---|------|-------------|
| 임시저장 | `draft` | 작성 중, 아직 미상신 | gray |
| 진행중 | `pending` | 결재 진행 중 | blue |
| 승인 | `approved` | 최종 승인 완료 | green |
| 반려 | `rejected` | 결재자가 반려 | red |
| 회수 | `cancelled` | 기안자가 회수 | yellow |
| 보류 | `on_hold` | 결재자가 보류 | orange |

### 2.2 결재 단계 유형

| 유형 | 값 | 설명 |
|------|---|------|
| 결재 | `approval` | 승인/반려 권한 |
| 합의 | `agreement` | 승인/반려 권한 (다른 부서) |
| 참조 | `reference` | 열람만 가능 |

### 2.3 결재 단계 상태

| 상태 | 값 | 설명 |
|------|---|------|
| 대기 | `pending` | 아직 미처리 |
| 승인 | `approved` | 승인함 |
| 반려 | `rejected` | 반려함 |
| 건너뜀 | `skipped` | 전결/회수로 건너뜀 |
| 보류 | `on_hold` | 보류 중 |

---

## 3. 목록 API (탭별)

### 3.1 기안함 (내가 기안한 문서)

```
GET /api/v1/approvals/drafts
```

| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|:----:|------|
| `status` | string | - | `draft`, `pending`, `approved`, `rejected`, `cancelled`, `on_hold` |
| `search` | string | - | 제목/문서번호 검색 |
| `sort_by` | string | - | `created_at`(기본), `drafted_at`, `completed_at`, `title`, `status` |
| `sort_dir` | string | - | `asc`, `desc`(기본) |
| `per_page` | int | - | 페이지당 건수 (기본 20) |
| `page` | int | - | 페이지 번호 |

**응답 데이터** (페이지네이션):

```json
{
  "data": [
    {
      "id": 1,
      "document_number": "APR-20260311-0001",
      "title": "출장 품의서",
      "status": "pending",
      "is_urgent": false,
      "drafted_at": "2026-03-11 10:00:00",
      "completed_at": null,
      "created_at": "2026-03-11",
      "form": {
        "id": 1,
        "name": "품의서",
        "code": "request",
        "category": "request"
      },
      "drafter": {
        "id": 10,
        "name": "홍길동",
        "tenant_profile": {
          "department": { "id": 3, "name": "영업부" },
          "position_key": "manager"
        }
      },
      "steps": [
        {
          "id": 1,
          "step_order": 1,
          "step_type": "approval",
          "status": "approved",
          "approver_name": "김팀장",
          "approver_department": "영업부",
          "approver_position": "팀장",
          "comment": null,
          "acted_at": "2026-03-11 11:00:00",
          "approver": {
            "id": 5,
            "name": "김팀장"
          }
        },
        {
          "id": 2,
          "step_order": 2,
          "step_type": "approval",
          "status": "pending",
          "approver_name": "박부장",
          "approver_department": "영업부",
          "approver_position": "부장",
          "approver": {
            "id": 8,
            "name": "박부장"
          }
        }
      ]
    }
  ],
  "current_page": 1,
  "last_page": 3,
  "per_page": 20,
  "total": 52
}
```

---

### 3.2 기안함 현황 카드

```
GET /api/v1/approvals/drafts/summary
```

파라미터 없음.

```json
{
  "total": 52,
  "draft": 3,
  "pending": 12,
  "approved": 30,
  "rejected": 7
}
```

---

### 3.3 결재함 (내가 결재할 문서)

```
GET /api/v1/approvals/inbox
```

| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|:----:|------|
| `status` | string | - | `requested`(내 차례), `scheduled`(예정), `completed`(처리완료), `rejected`(내가 반려) |
| `sort_by` | string | - | `created_at`(기본), `drafted_at`, `completed_at`, `title` |
| `sort_dir` | string | - | `asc`, `desc`(기본) |
| `start_date` | string | - | `YYYY-MM-DD` 시작일 |
| `end_date` | string | - | `YYYY-MM-DD` 종료일 |
| `per_page` | int | - | 페이지당 건수 |
| `page` | int | - | 페이지 번호 |

> **`requested`**: 현재 내가 처리해야 할 문서 (내 차례)
> **`scheduled`**: 아직 내 차례가 아닌 문서 (앞 결재자가 미처리)

---

### 3.4 결재함 현황 카드

```
GET /api/v1/approvals/inbox/summary
```

```json
{
  "requested": 5,
  "scheduled": 3,
  "completed": 20,
  "rejected": 2
}
```

---

### 3.5 참조함 (내가 참조된 문서)

```
GET /api/v1/approvals/reference
```

| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|:----:|------|
| `is_read` | boolean | - | `true`(읽음), `false`(안읽음) |
| `sort_by` | string | - | `created_at`(기본), `drafted_at`, `completed_at`, `title` |
| `sort_dir` | string | - | `asc`, `desc`(기본) |
| `per_page` | int | - | 페이지당 건수 |
| `page` | int | - | 페이지 번호 |

---

### 3.6 완료함 (내 기안 완료 + 내가 처리한 문서)

```
GET /api/v1/approvals/completed
```

IndexRequest와 동일한 파라미터 사용.

---

### 3.7 완료함 현황 카드

```
GET /api/v1/approvals/completed/summary
```

```json
{
  "approved": 25,
  "rejected": 5,
  "cancelled": 2,
  "unread": 3
}
```

---

### 3.8 뱃지 카운트 (사이드바/GNB용)

```
GET /api/v1/approvals/badge-counts
```

```json
{
  "pending": 5,
  "draft": 2,
  "reference_unread": 3,
  "completed_unread": 1
}
```

> **UI 활용**: 사이드바 메뉴 옆에 `pending` 뱃지, 참조 탭에 `reference_unread` 뱃지 등

---

## 4. 문서 CRUD

### 4.1 문서 상세

```
GET /api/v1/approvals/{id}
```

상세 응답에는 `form.template` (양식 필드 정의), 전체 `steps` 목록, 연결된 `linkable` 문서 데이터가 포함된다.

---

### 4.2 문서 생성 (임시저장 또는 즉시 상신)

```
POST /api/v1/approvals
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `form_id` | int | O* | 결재 양식 ID (`form_code`와 택1) |
| `form_code` | string | O* | 결재 양식 코드 (`form_id`와 택1) |
| `title` | string | O | 제목 (최대 200자) |
| `content` | object | O | 양식 데이터 (JSON) |
| `body` | string | - | 본문 HTML |
| `is_urgent` | boolean | - | 긴급 여부 |
| `department_id` | int | - | 기안 부서 ID |
| `line_id` | int | - | 결재선 템플릿 ID |
| `attachments` | int[] | - | 첨부파일 ID 배열 |
| `submit` | boolean | - | `true`면 즉시 상신 |
| `steps` | array | 조건* | 결재선 (`submit=true`일 때 필수) |

**steps 배열 항목**:

| 필드 | 타입 | 설명 |
|------|------|------|
| `step_type` (또는 `type`) | string | `approval`, `agreement`, `reference` |
| `approver_id` (또는 `user_id`) | int | 결재자 ID |
| `step_order` | int | 순서 (생략 시 자동 부여) |

```json
{
  "form_code": "request",
  "title": "출장 품의서",
  "content": {
    "destination": "부산",
    "purpose": "거래처 미팅",
    "amount": 500000
  },
  "submit": true,
  "steps": [
    { "step_type": "approval", "user_id": 5 },
    { "step_type": "approval", "user_id": 8 },
    { "step_type": "reference", "user_id": 12 }
  ]
}
```

> **참고**: `submit: false`이면 임시저장만 하고 `steps`는 선택 사항이다.

---

### 4.3 문서 수정 (임시저장 상태만)

```
PATCH /api/v1/approvals/{id}
```

모든 필드 선택적. `steps`를 전달하면 기존 결재선을 교체한다.

---

### 4.4 문서 삭제 (임시저장/반려 상태만)

```
DELETE /api/v1/approvals/{id}
```

---

## 5. 워크플로우 액션

### 5.1 상신 (임시저장 → 진행중)

```
POST /api/v1/approvals/{id}/submit
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `steps` | array | - | 새 결재선 (생략 시 기존 결재선 사용) |

> **재상신**: 반려 상태에서도 호출 가능. 반려 이력이 `rejection_history`에 자동 저장되고, `resubmit_count`가 1 증가한다.

---

### 5.2 승인

```
POST /api/v1/approvals/{id}/approve
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `comment` | string | - | 승인 의견 (최대 1000자) |

> 현재 내 차례인 결재 단계만 승인 가능. 마지막 결재자가 승인하면 문서 상태가 `approved`로 변경된다.

---

### 5.3 반려

```
POST /api/v1/approvals/{id}/reject
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `comment` | string | **O** | 반려 사유 (필수, 최대 1000자) |

---

### 5.4 회수 (기안자만)

```
POST /api/v1/approvals/{id}/cancel
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `recall_reason` | string | - | 회수 사유 (최대 1000자) |

> 첫 번째 결재자가 이미 처리한 경우 회수 불가 (에러 반환).

---

### 5.5 보류 (현재 결재자만)

```
POST /api/v1/approvals/{id}/hold
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `comment` | string | **O** | 보류 사유 (필수, 최대 1000자) |

---

### 5.6 보류 해제 (보류한 결재자만)

```
POST /api/v1/approvals/{id}/release-hold
```

파라미터 없음. 보류 상태를 다시 진행중으로 되돌린다.

---

### 5.7 전결 (현재 결재자가 최종 승인)

```
POST /api/v1/approvals/{id}/pre-decide
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `comment` | string | - | 전결 의견 (최대 1000자) |

> 현재 결재자 이후의 모든 단계를 `skipped`로 변경하고 문서를 `approved`로 확정한다.

---

### 5.8 복사 재기안

```
POST /api/v1/approvals/{id}/copy
```

파라미터 없음. 완료/반려/회수된 문서를 복사하여 새 임시저장 문서를 생성한다. 결재선(스냅샷 포함)도 함께 복사된다.

**응답**: 새로 생성된 Approval 객체

---

## 6. 참조 열람 관리

### 6.1 참조 열람 처리

```
POST /api/v1/approvals/{id}/read
```

### 6.2 참조 미열람 처리

```
POST /api/v1/approvals/{id}/unread
```

### 6.3 완료함 일괄 읽음 처리

```
POST /api/v1/approvals/completed/mark-read
```

> 기안자의 미읽음 완료 문서를 일괄로 읽음 처리한다.

---

## 7. 위임 관리

부재 시 다른 사용자에게 결재 권한을 위임한다.

### 7.1 위임 목록

```
GET /api/v1/approvals/delegations
```

---

### 7.2 위임 생성

```
POST /api/v1/approvals/delegations
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `delegate_id` | int | **O** | 대리자 ID |
| `start_date` | string | **O** | 시작일 (`YYYY-MM-DD`, 오늘 이후) |
| `end_date` | string | **O** | 종료일 (`YYYY-MM-DD`, 시작일 이후) |
| `form_ids` | int[] | - | 특정 양식만 위임 (생략 시 전체) |
| `notify_delegator` | boolean | - | 위임자에게 알림 여부 |
| `reason` | string | - | 위임 사유 (최대 500자) |

---

### 7.3 위임 수정

```
PATCH /api/v1/approvals/delegations/{id}
```

모든 필드 선택적. `is_active: false`로 비활성화 가능.

---

### 7.4 위임 삭제

```
DELETE /api/v1/approvals/delegations/{id}
```

---

## 8. 결재 양식 API

### 8.1 양식 목록

```
GET /api/v1/approval-forms
```

### 8.2 활성 양식 목록 (기안 시 드롭다운용)

```
GET /api/v1/approval-forms/active
```

### 8.3 양식 상세

```
GET /api/v1/approval-forms/{id}
```

### 8.4 양식 생성

```
POST /api/v1/approval-forms
```

### 8.5 양식 수정

```
PATCH /api/v1/approval-forms/{id}
```

### 8.6 양식 삭제

```
DELETE /api/v1/approval-forms/{id}
```

---

## 9. 결재선 템플릿 API

### 9.1 결재선 목록

```
GET /api/v1/approval-lines
```

### 9.2 결재선 상세

```
GET /api/v1/approval-lines/{id}
```

### 9.3 결재선 생성

```
POST /api/v1/approval-lines
```

### 9.4 결재선 수정

```
PATCH /api/v1/approval-lines/{id}
```

### 9.5 결재선 삭제

```
DELETE /api/v1/approval-lines/{id}
```

---

## 10. 화면 구성 가이드

### 10.1 메인 탭 구조

```
┌─────────┬─────────┬─────────┬─────────┐
│ 기안함  │ 결재함  │ 참조함  │ 완료함  │
│ (drafts)│ (inbox) │(reference│(completed│
│         │  [5]    │) [3]    │) [1]    │
└─────────┴─────────┴─────────┴─────────┘
```

- 각 탭의 뱃지 숫자는 `GET /badge-counts`로 가져온다
- `pending` → 결재함 뱃지
- `reference_unread` → 참조함 뱃지
- `completed_unread` → 완료함 뱃지

### 10.2 결재함 서브 필터

| 필터 | status 값 | 설명 |
|------|----------|------|
| 결재 요청 | `requested` | 현재 내 차례 (액션 버튼 표시) |
| 예정 | `scheduled` | 내 차례 대기 |
| 처리 완료 | `completed` | 내가 처리한 문서 |
| 반려 | `rejected` | 내가 반려한 문서 |

### 10.3 문서 상세 페이지 액션 버튼

| 조건 | 표시할 버튼 |
|------|-----------|
| 내가 기안자 + `draft`/`rejected` | 수정, 삭제, 상신 |
| 내가 기안자 + `pending`/`on_hold` | 회수 |
| 내가 현재 결재자 + `pending` | 승인, 반려, 보류, 전결 |
| 내가 보류한 결재자 + `on_hold` | 보류해제 |
| `approved`/`rejected`/`cancelled` | 복사 재기안 |
| 내가 참조자 | 열람/미열람 토글 |

### 10.4 결재선 표시

결재 단계를 순서대로 표시한다. 각 단계에 스냅샷 정보를 사용한다:

```
[1] 결재 | 김팀장 (영업부/팀장) → 승인 ✓ (2026-03-11 11:00)
[2] 결재 | 박부장 (영업부/부장) → 대기 중 ◯
[3] 합의 | 이과장 (재무부/과장) → 대기 중 ◯
[4] 참조 | 정대리 (인사부/대리) → 미열람
```

> **스냅샷 필드**: `approver_name`, `approver_department`, `approver_position`은 결재선 생성 시점의 정보가 저장된다. 결재자가 부서 이동해도 결재 당시 정보가 유지된다.

---

## 관련 문서

- `dev/dev_plans/approval-system-unification-plan.md` - 결재 시스템 통합 계획서
- `frontend/api-specs/document-api-integration.md` - 문서 API 연동 명세

---

**최종 업데이트**: 2026-03-11
