| 2026-06-25T06:21:53 |
Aha00a |
5
|
0
|
= Api |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
1
|
|
| 2026-06-25T06:21:53 |
Aha00a |
5
|
2
|
AhaWiki 페이지를 외부 사용자, 자동화 도구, AI 에이전트가 API Key로 읽고 저장하기 위한 API입니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
3
|
|
| 2026-06-25T06:21:53 |
Aha00a |
5
|
4
|
AhaWiki API는 세션 쿠키 대신 `Authorization: Bearer <key>` 헤더를 사용합니다. |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
5
|
AhaWiki API 요청에는 CSRF token과 reCAPTCHA가 필요하지 않습니다. |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
6
|
읽기/쓰기 endpoint path는 `/api/v1/` prefix를 사용합니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
7
|
|
| 2026-06-25T06:21:53 |
Aha00a |
5
|
8
|
== 지원 기능 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
9
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
10
|
* API Key 인증 사용자로 페이지 읽기 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
11
|
* API Key 인증 사용자로 페이지 저장 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
12
|
* API Key 인증 사용자로 페이지 목록/메타 조회 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
13
|
* API Key 인증 사용자로 최근 변경 조회 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
14
|
* API Key 인증 사용자로 페이지 이름변경/삭제 |
| 2026-06-28T05:07:02 |
Aha00a |
9
|
15
|
* 저장된 revision에 `viaApi = true`와 저장에 사용한 API Key(`userApiKey`) 기록 |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
16
|
* `/api/v1/changes` 응답과 History·RecentChanges 화면에 `viaApi`와 key 이름(`userApiKeyName`) 표시 — 아래 '''변경 이력 표시와 필터''' |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
17
|
|
| 2026-06-25T06:21:53 |
Aha00a |
5
|
18
|
== 제한사항 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
19
|
|
| 2026-09-12T09:48:08 |
Aha00a |
13
|
20
|
* API로 저장·이름변경·삭제하면 텔레그램 알림이 가지 않습니다. API 저장은 그 페이지를 열어 둔 브라우저에 실시간 갱신 알림도 보내지 않습니다. |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
21
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
22
|
== 인증 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
23
|
|
| 2026-06-25T06:21:53 |
Aha00a |
5
|
24
|
AhaWiki API 호출에는 아래 헤더를 넣습니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
25
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
26
|
[[[#!Vim text |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
27
|
Authorization: Bearer ahawiki_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
28
|
]]] |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
29
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
30
|
API Key는 생성 화면에서 한 번만 plain text로 표시됩니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
31
|
이후 목록 조회에서는 `keyPrefix`만 볼 수 있고 원본 key는 다시 조회할 수 없습니다. |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
32
|
API Key는 Account Settings의 API Keys 섹션에서 생성하고 폐기할 수 있습니다. |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
33
|
키가 유출되면 Account Settings에서 폐기해야 합니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
34
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
35
|
이 문서는 `Authorization: Bearer <api-key>`로 호출하는 API만 다룹니다. |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
36
|
브라우저 세션과 CSRF token이 필요한 Account/Admin API는 공개 API로 다루지 않습니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
37
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
38
|
== 페이지 읽기 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
39
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
40
|
[[[#!Vim sh |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
41
|
curl https://ahawiki.net/api/v1/page/FrontPage \ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
42
|
-H "Authorization: Bearer <api-key>" |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
43
|
]]] |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
44
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
45
|
응답: |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
46
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
47
|
[[[#!Vim json |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
48
|
{ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
49
|
"name": "FrontPage", |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
50
|
"revision": 12, |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
51
|
"content": "= FrontPage\n...", |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
52
|
"dateTime": "2026-06-24T10:00:00", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
53
|
"isMinorEdit": false, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
54
|
"viaApi": false, |
| 2026-06-28T05:07:02 |
Aha00a |
9
|
55
|
"userApiKeyName": null, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
56
|
"contentHash": "sha256:..." |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
57
|
} |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
58
|
]]] |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
59
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
60
|
권한은 API Key 소유자의 기존 `WikiPermission`을 따릅니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
61
|
읽기 권한이 없으면 `403 Forbidden`을 반환합니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
62
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
63
|
== 페이지 목록/메타 조회 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
64
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
65
|
[[[#!Vim sh |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
66
|
curl "https://ahawiki.net/api/v1/pages?prefix=ToDo&limit=100" \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
67
|
-H "Authorization: Bearer <api-key>" |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
68
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
69
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
70
|
응답: |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
71
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
72
|
[[[#!Vim json |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
73
|
{ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
74
|
"pages": [ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
75
|
{ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
76
|
"name": "ToDo", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
77
|
"revision": 81, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
78
|
"dateTime": "2026-06-24T22:00:00", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
79
|
"isMinorEdit": false, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
80
|
"viaApi": true, |
| 2026-06-28T05:07:02 |
Aha00a |
9
|
81
|
"userApiKeyName": "AhaWikiDoc", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
82
|
"size": 3223, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
83
|
"contentHash": "sha256:..." |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
84
|
} |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
85
|
] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
86
|
} |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
87
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
88
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
89
|
`contentHash`는 전체 본문을 내려받지 않고 local/remote 차이를 빠르게 비교하기 위한 SHA-256 hash입니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
90
|
응답은 API Key 소유자가 읽을 수 있는 페이지로 제한됩니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
91
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
92
|
== Batch 페이지 메타 조회 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
93
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
94
|
[[[#!Vim sh |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
95
|
curl -X POST https://ahawiki.net/api/v1/pages/metadata \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
96
|
-H "Authorization: Bearer <api-key>" \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
97
|
-H "Content-Type: application/json" \ |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
98
|
-d '{"names":["Api","Dev Api","MissingPage"]}' |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
99
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
100
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
101
|
응답: |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
102
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
103
|
[[[#!Vim json |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
104
|
{ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
105
|
"pages": [ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
106
|
{ |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
107
|
"name": "Api", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
108
|
"revision": 1, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
109
|
"dateTime": "2026-06-24T22:20:29", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
110
|
"isMinorEdit": false, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
111
|
"viaApi": true, |
| 2026-06-28T05:07:02 |
Aha00a |
9
|
112
|
"userApiKeyName": "AhaWikiDoc", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
113
|
"size": 5548, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
114
|
"contentHash": "sha256:..." |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
115
|
} |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
116
|
], |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
117
|
"missing": ["MissingPage"], |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
118
|
"forbidden": [] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
119
|
} |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
120
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
121
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
122
|
== 페이지 저장 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
123
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
124
|
[[[#!Vim sh |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
125
|
curl -X POST https://ahawiki.net/api/v1/page/FrontPage \ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
126
|
-H "Authorization: Bearer <api-key>" \ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
127
|
-H "Content-Type: application/json" \ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
128
|
-d '{ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
129
|
"revision": 12, |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
130
|
"text": "= FrontPage\nupdated content", |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
131
|
"comment": "Update via API", |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
132
|
"minorEdit": false |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
133
|
}' |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
134
|
]]] |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
135
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
136
|
요청 필드: |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
137
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
138
|
* `revision` — 저장 기준 revision. 최신 revision과 다르면 저장하지 않습니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
139
|
* `text` — 저장할 전체 페이지 본문. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
140
|
* `comment` — 저장 comment. 생략하면 빈 문자열입니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
141
|
* `minorEdit` — minor edit 여부. 생략하면 `false`입니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
142
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
143
|
응답: |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
144
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
145
|
[[[#!Vim json |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
146
|
{ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
147
|
"name": "FrontPage", |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
148
|
"revision": 13, |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
149
|
"dateTime": "2026-06-24T10:01:00" |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
150
|
} |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
151
|
]]] |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
152
|
|
| 2026-09-12T09:48:08 |
Aha00a |
13
|
153
|
저장 성공 시 새 revision은 `viaApi = true`로 기록됩니다. 웹 편집과 달리 알림을 보내지 않습니다(위 '''제한사항'''). |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
154
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
155
|
== 페이지 이름변경 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
156
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
157
|
[[[#!Vim sh |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
158
|
curl -X POST https://ahawiki.net/api/v1/rename \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
159
|
-H "Authorization: Bearer <api-key>" \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
160
|
-H "Content-Type: application/json" \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
161
|
-d '{ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
162
|
"name": "OldPage", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
163
|
"newName": "NewPage", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
164
|
"revision": 3, |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
165
|
"comment": "Rename via API" |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
166
|
}' |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
167
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
168
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
169
|
요청 revision이 최신 revision과 다르면 `409 Conflict`를 반환합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
170
|
대상 페이지가 이미 있으면 `409 Conflict`를 반환합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
171
|
성공하면 기존 페이지 이름을 `newName`으로 바꾸고, 기존 이름에는 redirect page를 생성합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
172
|
redirect revision은 `viaApi = true`로 기록됩니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
173
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
174
|
== 페이지 삭제 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
175
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
176
|
[[[#!Vim sh |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
177
|
curl -X DELETE https://ahawiki.net/api/v1/page/OldPage \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
178
|
-H "Authorization: Bearer <api-key>" \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
179
|
-H "Content-Type: application/json" \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
180
|
-d '{ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
181
|
"revision": 3, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
182
|
"confirm": true |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
183
|
}' |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
184
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
185
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
186
|
삭제는 되돌리기 어려운 작업이므로 `confirm: true`가 필요합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
187
|
요청 revision이 최신 revision과 다르면 `409 Conflict`를 반환합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
188
|
삭제 성공 시 페이지에 연결된 첨부파일도 웹 삭제와 같은 정책으로 삭제 처리합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
189
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
190
|
== 최근 변경 조회 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
191
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
192
|
[[[#!Vim sh |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
193
|
curl "https://ahawiki.net/api/v1/changes?prefix=ToDo&since=2026-06-24T00:00:00&limit=100" \ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
194
|
-H "Authorization: Bearer <api-key>" |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
195
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
196
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
197
|
query parameter: |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
198
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
199
|
* `name` — 특정 page name만 조회. `afterRevision`을 사용할 때 필요합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
200
|
* `prefix` — page name prefix 필터 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
201
|
* `since` — ISO-8601 local date-time 이후 변경만 조회 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
202
|
* `afterRevision` — `name`으로 지정한 단일 페이지에서 해당 revision 초과 변경만 조회 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
203
|
* `includeMinorEdit` — `0`이면 minor edit 제외, 기본값은 `1` |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
204
|
* `includeViaApi` — `0`이면 API Key 저장 제외, 기본값은 `1` |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
205
|
* `limit` — 최대 행 수, 기본값은 `100` |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
206
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
207
|
`revision`은 페이지별 번호이므로 `afterRevision`은 전체 sync cursor로 사용할 수 없습니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
208
|
전체 동기화는 `since`와 페이지별 metadata 비교를 함께 사용합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
209
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
210
|
응답: |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
211
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
212
|
[[[#!Vim json |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
213
|
{ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
214
|
"changes": [ |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
215
|
{ |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
216
|
"name": "Api", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
217
|
"revision": 2, |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
218
|
"dateTime": "2026-06-24T22:00:00", |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
219
|
"comment": "AhaWikiDoc sync Api", |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
220
|
"isMinorEdit": false, |
| 2026-06-28T05:07:02 |
Aha00a |
9
|
221
|
"viaApi": true, |
| 2026-06-28T05:07:02 |
Aha00a |
9
|
222
|
"userApiKeyName": "AhaWikiDoc" |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
223
|
} |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
224
|
] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
225
|
} |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
226
|
]]] |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
227
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
228
|
== AhaWikiDoc sync 권장 방식 |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
229
|
|
| 2026-06-25T00:46:14 |
Aha00a |
2
|
230
|
문서 동기화 도구는 서버에 별도 sync state를 저장하지 않고 local manifest를 갱신하는 방식을 권장합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
231
|
|
| 2026-06-25T06:54:55 |
Aha00a |
7
|
232
|
1. `GET /api/v1/pages`로 원격 페이지의 `revision`, `dateTime`, `contentHash`를 조회합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
233
|
2. local manifest의 마지막 동기화 시각 또는 page별 revision과 비교합니다. |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
234
|
3. 변경 후보만 `GET /api/v1/page/*name`으로 본문을 내려받아 local file과 비교합니다. |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
235
|
4. local에서 검토/커밋된 문서가 원격보다 최신이면 `POST /api/v1/page/*name`으로 저장합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
236
|
5. 원격이 최신이면 local file을 갱신합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
237
|
6. 양쪽이 모두 바뀐 경우 자동 덮어쓰기를 하지 말고 병합하거나 사용자에게 확인합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
238
|
7. 저장/가져오기 완료 후 local manifest에 `lastSyncedAt`, page별 `revision`, `dateTime`, `contentHash`를 기록합니다. |
| 2026-06-25T00:46:14 |
Aha00a |
2
|
239
|
|
| 2026-09-12T09:48:08 |
Aha00a |
13
|
240
|
'''이 저장소의 참조 구현은 2·3·5·7번을 하지 않습니다.''' `scripts/sync.ahawiki.net.mjs`는 별도 manifest 대신 git 히스토리를 마지막 동기화 기록으로 쓰고, 본문을 내려받아 비교하지 않고 목록의 `contentHash`만 비교합니다 — 원격 본문이 그 파일의 최근 커밋 40개 중 '''예전 버전'''과 같으면 로컬이 앞선 것으로 보고 `--apply`일 때 저장하고, 그렇지 않으면 브라우저에서 편집된 것으로 보고 diverged로 알리기만 합니다. 로컬 파일은 바꾸지 않습니다 — diverged는 사람이 병합해 커밋하고, 로컬에 없는 페이지는 `download:ahawiki.net`으로 받습니다. 문서가 이미 git 으로 관리되므로 같은 사실을 manifest 에 한 번 더 적으면 그 둘이 갈라지기 때문입니다. manifest 방식은 문서가 git 밖에 있는 도구에 유효합니다. |
| 2026-08-31T05:03:15 |
Aha00a |
10
|
241
|
|
| 2026-08-31T05:03:15 |
Aha00a |
10
|
242
|
`docs/ahawiki.net/manifest.json`은 sync state가 아니라 `download:ahawiki.net`이 남기는 내려받기 기록입니다. |
| 2026-08-31T05:03:15 |
Aha00a |
10
|
243
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
244
|
== 변경 이력 표시와 필터 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
245
|
|
| 2026-06-28T05:07:02 |
Aha00a |
9
|
246
|
API Key로 저장한 revision은 `viaApi = true`로 기록되고, 저장에 사용한 key는 `userApiKey`로 기록됩니다. |
| 2026-09-10T19:36:44 |
Aha00a |
12
|
247
|
응답의 `userApiKeyName`은 저장에 쓴 key의 현재 이름입니다. 웹에서 저장한 revision은 `null`입니다. key를 폐기해도 이름은 그대로 나옵니다 — 폐기는 key를 못 쓰게 할 뿐 기록을 지우지 않습니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
248
|
|
| 2026-06-28T05:07:02 |
Aha00a |
9
|
249
|
* History 화면에는 minor edit, Via API 이모지 컬럼과 `Show ViaApi` 필터가 있고, Via API 편집에는 key 이름이 함께 표시됩니다. |
| 2026-09-24T12:44:46 |
Aha00a |
15
|
250
|
* `RecentChanges` 매크로에는 `Include via API edits` 토글과 minor edit, Via API 이모지 컬럼이 있습니다. 토글은 처음에 켜져 있습니다([MacroRecentChanges]). |
| 2026-06-25T05:51:25 |
Aha00a |
4
|
251
|
* `/api/change`는 `includeViaApi` 파라미터를 받습니다. 기본값은 `0`입니다. |
| 2026-06-25T05:51:25 |
Aha00a |
4
|
252
|
* Admin Recent Changes 화면에는 viaApi 포함/제외 토글과 minor edit, Via API 이모지 컬럼이 있습니다. |
| 2026-09-13T03:56:11 |
Aha00a |
14
|
253
|
* Admin Dashboard의 Recent Changes 요약 표도 minor edit와 API 편집을 함께 불러와, 같은 이모지 컬럼으로 표시합니다. 2026-09-13 전에는 둘을 빼고 불러와서 두 컬럼이 늘 비어 있었습니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
254
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
255
|
== 저장 Flow |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
256
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
257
|
자동화 도구는 아래 순서로 저장합니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
258
|
|
| 2026-09-12T09:48:08 |
Aha00a |
13
|
259
|
1. `GET /api/v1/page/*name`으로 현재 본문과 `revision`을 읽습니다. |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
260
|
2. 본문을 수정합니다. |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
261
|
3. `POST /api/v1/page/*name`에 읽었던 revision과 수정된 전체 text를 함께 보냅니다. |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
262
|
4. `409 Conflict`가 오면 최신 본문을 다시 읽고 변경 내용을 병합한 뒤 재시도합니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
263
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
264
|
== AI 자동화 프롬프트 예시 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
265
|
|
| 2026-06-25T06:21:53 |
Aha00a |
5
|
266
|
아래 프롬프트를 AI 코딩 에이전트나 자동화 도구에 전달하면 AhaWiki API로 페이지를 읽고 수정하게 할 수 있습니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
267
|
`<api-key>`, `<page-name>`, `<task>`는 실제 값으로 바꿉니다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
268
|
|
| 2026-06-25T06:39:55 |
Aha00a |
6
|
269
|
[[[#!Vim text |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
270
|
너는 AhaWiki 문서를 수정하는 자동화 에이전트다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
271
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
272
|
인증: |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
273
|
- 모든 AhaWiki API 요청에 `Authorization: Bearer <api-key>` 헤더를 넣어라. |
| 2026-06-25T06:21:53 |
Aha00a |
5
|
274
|
- CSRF token, session cookie, reCAPTCHA는 AhaWiki API에 필요 없다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
275
|
- API Key를 로그, 문서 본문, 저장 comment에 노출하지 마라. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
276
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
277
|
대상: |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
278
|
- Base URL: https://ahawiki.net |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
279
|
- Page: <page-name> |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
280
|
- 작업: <task> |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
281
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
282
|
읽기: |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
283
|
1. `GET https://ahawiki.net/api/v1/page/<url-encoded-page-name>`를 호출한다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
284
|
2. 응답의 `revision`과 `content`를 저장한다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
285
|
3. `403`이면 권한 없음으로 중단하고, `404`이면 페이지 없음으로 보고한다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
286
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
287
|
수정: |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
288
|
1. 기존 `content`를 기준으로 필요한 부분만 바꾼 전체 본문을 만든다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
289
|
2. 관련 없는 내용, 포맷, 줄바꿈은 가능한 유지한다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
290
|
3. 저장 comment는 사람이 이해할 수 있게 짧게 쓴다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
291
|
4. 사소한 포맷/오타 수정이면 `minorEdit: true`, 의미 있는 내용 변경이면 `minorEdit: false`로 보낸다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
292
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
293
|
저장: |
| 2026-06-25T06:54:55 |
Aha00a |
7
|
294
|
`POST https://ahawiki.net/api/v1/page/<url-encoded-page-name>`에 아래 JSON을 보낸다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
295
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
296
|
{ |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
297
|
"revision": <읽은 revision>, |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
298
|
"text": "<수정된 전체 본문>", |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
299
|
"comment": "<저장 comment>", |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
300
|
"minorEdit": false |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
301
|
} |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
302
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
303
|
충돌 처리: |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
304
|
- `409 Conflict`가 오면 최신 페이지를 다시 읽고 내 변경사항을 최신 본문에 병합한 뒤 한 번 더 저장한다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
305
|
- 병합이 애매하면 저장하지 말고 충돌 내용을 보고한다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
306
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
307
|
완료 보고: |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
308
|
- 저장 성공 시 새 revision을 보고한다. |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
309
|
- 저장하지 못하면 HTTP status와 원인을 보고한다. |
| 2026-06-25T06:39:55 |
Aha00a |
6
|
310
|
]]] |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
311
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
312
|
== 에러 코드 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
313
|
|
| 2026-06-25T07:05:23 |
Aha00a |
8
|
314
|
[[[#!Table tsv 1 |
| 2026-06-25T07:05:23 |
Aha00a |
8
|
315
|
코드 의미 |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
316
|
`400 Bad Request` JSON body 없음, `revision`·`text` 누락, 본문 변경 없음, 삭제에 `confirm: true` 없음, 이름변경에 `name`·`newName` 없음, `since` 형식 오류, `name` 없이 `afterRevision` 사용 |
| 2026-06-25T07:05:23 |
Aha00a |
8
|
317
|
`401 Unauthorized` API Key 없음 또는 유효하지 않음 |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
318
|
`403 Forbidden` API Key 소유자에게 그 작업의 권한 없음 |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
319
|
`404 Not Found` 읽기·이름변경·삭제 대상 페이지가 없음 |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
320
|
`409 Conflict` 요청 revision이 최신 revision과 다름(응답에 `latestRevision` 포함), 또는 이름변경 대상 이름이 이미 있음 |
| 2026-09-12T09:48:08 |
Aha00a |
13
|
321
|
`500 Internal Server Error` 삭제 중 첨부파일 삭제 실패 |
| 2026-06-25T07:05:23 |
Aha00a |
8
|
322
|
]]] |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
323
|
|
| 2026-06-24T22:20:29 |
Aha00a |
1
|
324
|
== 관련 |
| 2026-06-24T22:20:29 |
Aha00a |
1
|
325
|
|
| 2026-09-05T12:16:02 |
Aha00a |
11
|
326
|
* ["Dev Api"] |