kollabor 0.4.9__py3-none-any.whl → 0.4.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agents/__init__.py +2 -0
- agents/coder/__init__.py +0 -0
- agents/coder/agent.json +4 -0
- agents/coder/api-integration.md +2150 -0
- agents/coder/cli-pretty.md +765 -0
- agents/coder/code-review.md +1092 -0
- agents/coder/database-design.md +1525 -0
- agents/coder/debugging.md +1102 -0
- agents/coder/dependency-management.md +1397 -0
- agents/coder/git-workflow.md +1099 -0
- agents/coder/refactoring.md +1454 -0
- agents/coder/security-hardening.md +1732 -0
- agents/coder/system_prompt.md +1448 -0
- agents/coder/tdd.md +1367 -0
- agents/creative-writer/__init__.py +0 -0
- agents/creative-writer/agent.json +4 -0
- agents/creative-writer/character-development.md +1852 -0
- agents/creative-writer/dialogue-craft.md +1122 -0
- agents/creative-writer/plot-structure.md +1073 -0
- agents/creative-writer/revision-editing.md +1484 -0
- agents/creative-writer/system_prompt.md +690 -0
- agents/creative-writer/worldbuilding.md +2049 -0
- agents/data-analyst/__init__.py +30 -0
- agents/data-analyst/agent.json +4 -0
- agents/data-analyst/data-visualization.md +992 -0
- agents/data-analyst/exploratory-data-analysis.md +1110 -0
- agents/data-analyst/pandas-data-manipulation.md +1081 -0
- agents/data-analyst/sql-query-optimization.md +881 -0
- agents/data-analyst/statistical-analysis.md +1118 -0
- agents/data-analyst/system_prompt.md +928 -0
- agents/default/__init__.py +0 -0
- agents/default/agent.json +4 -0
- agents/default/dead-code.md +794 -0
- agents/default/explore-agent-system.md +585 -0
- agents/default/system_prompt.md +1448 -0
- agents/kollabor/__init__.py +0 -0
- agents/kollabor/analyze-plugin-lifecycle.md +175 -0
- agents/kollabor/analyze-terminal-rendering.md +388 -0
- agents/kollabor/code-review.md +1092 -0
- agents/kollabor/debug-mcp-integration.md +521 -0
- agents/kollabor/debug-plugin-hooks.md +547 -0
- agents/kollabor/debugging.md +1102 -0
- agents/kollabor/dependency-management.md +1397 -0
- agents/kollabor/git-workflow.md +1099 -0
- agents/kollabor/inspect-llm-conversation.md +148 -0
- agents/kollabor/monitor-event-bus.md +558 -0
- agents/kollabor/profile-performance.md +576 -0
- agents/kollabor/refactoring.md +1454 -0
- agents/kollabor/system_prompt copy.md +1448 -0
- agents/kollabor/system_prompt.md +757 -0
- agents/kollabor/trace-command-execution.md +178 -0
- agents/kollabor/validate-config.md +879 -0
- agents/research/__init__.py +0 -0
- agents/research/agent.json +4 -0
- agents/research/architecture-mapping.md +1099 -0
- agents/research/codebase-analysis.md +1077 -0
- agents/research/dependency-audit.md +1027 -0
- agents/research/performance-profiling.md +1047 -0
- agents/research/security-review.md +1359 -0
- agents/research/system_prompt.md +492 -0
- agents/technical-writer/__init__.py +0 -0
- agents/technical-writer/agent.json +4 -0
- agents/technical-writer/api-documentation.md +2328 -0
- agents/technical-writer/changelog-management.md +1181 -0
- agents/technical-writer/readme-writing.md +1360 -0
- agents/technical-writer/style-guide.md +1410 -0
- agents/technical-writer/system_prompt.md +653 -0
- agents/technical-writer/tutorial-creation.md +1448 -0
- core/__init__.py +0 -2
- core/application.py +343 -88
- core/cli.py +229 -10
- core/commands/menu_renderer.py +463 -59
- core/commands/registry.py +14 -9
- core/commands/system_commands.py +2461 -14
- core/config/loader.py +151 -37
- core/config/service.py +18 -6
- core/events/bus.py +29 -9
- core/events/executor.py +205 -75
- core/events/models.py +27 -8
- core/fullscreen/command_integration.py +20 -24
- core/fullscreen/components/__init__.py +10 -1
- core/fullscreen/components/matrix_components.py +1 -2
- core/fullscreen/components/space_shooter_components.py +654 -0
- core/fullscreen/plugin.py +5 -0
- core/fullscreen/renderer.py +52 -13
- core/fullscreen/session.py +52 -15
- core/io/__init__.py +29 -5
- core/io/buffer_manager.py +6 -1
- core/io/config_status_view.py +7 -29
- core/io/core_status_views.py +267 -347
- core/io/input/__init__.py +25 -0
- core/io/input/command_mode_handler.py +711 -0
- core/io/input/display_controller.py +128 -0
- core/io/input/hook_registrar.py +286 -0
- core/io/input/input_loop_manager.py +421 -0
- core/io/input/key_press_handler.py +502 -0
- core/io/input/modal_controller.py +1011 -0
- core/io/input/paste_processor.py +339 -0
- core/io/input/status_modal_renderer.py +184 -0
- core/io/input_errors.py +5 -1
- core/io/input_handler.py +211 -2452
- core/io/key_parser.py +7 -0
- core/io/layout.py +15 -3
- core/io/message_coordinator.py +111 -2
- core/io/message_renderer.py +129 -4
- core/io/status_renderer.py +147 -607
- core/io/terminal_renderer.py +97 -51
- core/io/terminal_state.py +21 -4
- core/io/visual_effects.py +816 -165
- core/llm/agent_manager.py +1063 -0
- core/llm/api_adapters/__init__.py +44 -0
- core/llm/api_adapters/anthropic_adapter.py +432 -0
- core/llm/api_adapters/base.py +241 -0
- core/llm/api_adapters/openai_adapter.py +326 -0
- core/llm/api_communication_service.py +167 -113
- core/llm/conversation_logger.py +322 -16
- core/llm/conversation_manager.py +556 -30
- core/llm/file_operations_executor.py +84 -32
- core/llm/llm_service.py +934 -103
- core/llm/mcp_integration.py +541 -57
- core/llm/message_display_service.py +135 -18
- core/llm/plugin_sdk.py +1 -2
- core/llm/profile_manager.py +1183 -0
- core/llm/response_parser.py +274 -56
- core/llm/response_processor.py +16 -3
- core/llm/tool_executor.py +6 -1
- core/logging/__init__.py +2 -0
- core/logging/setup.py +34 -6
- core/models/resume.py +54 -0
- core/plugins/__init__.py +4 -2
- core/plugins/base.py +127 -0
- core/plugins/collector.py +23 -161
- core/plugins/discovery.py +37 -3
- core/plugins/factory.py +6 -12
- core/plugins/registry.py +5 -17
- core/ui/config_widgets.py +128 -28
- core/ui/live_modal_renderer.py +2 -1
- core/ui/modal_actions.py +5 -0
- core/ui/modal_overlay_renderer.py +0 -60
- core/ui/modal_renderer.py +268 -7
- core/ui/modal_state_manager.py +29 -4
- core/ui/widgets/base_widget.py +7 -0
- core/updates/__init__.py +10 -0
- core/updates/version_check_service.py +348 -0
- core/updates/version_comparator.py +103 -0
- core/utils/config_utils.py +685 -526
- core/utils/plugin_utils.py +1 -1
- core/utils/session_naming.py +111 -0
- fonts/LICENSE +21 -0
- fonts/README.md +46 -0
- fonts/SymbolsNerdFont-Regular.ttf +0 -0
- fonts/SymbolsNerdFontMono-Regular.ttf +0 -0
- fonts/__init__.py +44 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/METADATA +54 -4
- kollabor-0.4.15.dist-info/RECORD +228 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/top_level.txt +2 -0
- plugins/agent_orchestrator/__init__.py +39 -0
- plugins/agent_orchestrator/activity_monitor.py +181 -0
- plugins/agent_orchestrator/file_attacher.py +77 -0
- plugins/agent_orchestrator/message_injector.py +135 -0
- plugins/agent_orchestrator/models.py +48 -0
- plugins/agent_orchestrator/orchestrator.py +403 -0
- plugins/agent_orchestrator/plugin.py +976 -0
- plugins/agent_orchestrator/xml_parser.py +191 -0
- plugins/agent_orchestrator_plugin.py +9 -0
- plugins/enhanced_input/box_styles.py +1 -0
- plugins/enhanced_input/color_engine.py +19 -4
- plugins/enhanced_input/config.py +2 -2
- plugins/enhanced_input_plugin.py +61 -11
- plugins/fullscreen/__init__.py +6 -2
- plugins/fullscreen/example_plugin.py +1035 -222
- plugins/fullscreen/setup_wizard_plugin.py +592 -0
- plugins/fullscreen/space_shooter_plugin.py +131 -0
- plugins/hook_monitoring_plugin.py +436 -78
- plugins/query_enhancer_plugin.py +66 -30
- plugins/resume_conversation_plugin.py +1494 -0
- plugins/save_conversation_plugin.py +98 -32
- plugins/system_commands_plugin.py +70 -56
- plugins/tmux_plugin.py +154 -78
- plugins/workflow_enforcement_plugin.py +94 -92
- system_prompt/default.md +952 -886
- core/io/input_mode_manager.py +0 -402
- core/io/modal_interaction_handler.py +0 -315
- core/io/raw_input_processor.py +0 -946
- core/storage/__init__.py +0 -5
- core/storage/state_manager.py +0 -84
- core/ui/widget_integration.py +0 -222
- core/utils/key_reader.py +0 -171
- kollabor-0.4.9.dist-info/RECORD +0 -128
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/WHEEL +0 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/entry_points.txt +0 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,2328 @@
|
|
|
1
|
+
<!-- API Documentation skill - write comprehensive API documentation from scratch -->
|
|
2
|
+
|
|
3
|
+
api-documentation mode: DOCUMENT EVERY ENDPOINT
|
|
4
|
+
|
|
5
|
+
when this skill is active, you follow API documentation best practices.
|
|
6
|
+
this is a comprehensive guide to writing world-class API documentation.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
PHASE 0: PREREQUISITE DISCOVERY
|
|
10
|
+
|
|
11
|
+
before writing ANY API documentation, discover what exists.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
check for existing API specifications
|
|
15
|
+
|
|
16
|
+
<terminal>find . -name "openapi.yaml" -o -name "openapi.yml" -o -name "swagger.json" 2>/dev/null</terminal>
|
|
17
|
+
<terminal>find . -name "*.spec.yaml" -o -name "*.spec.yml" 2>/dev/null</terminal>
|
|
18
|
+
|
|
19
|
+
if OpenAPI spec exists:
|
|
20
|
+
<read><file>openapi.yaml</file></read>
|
|
21
|
+
analyze current completeness
|
|
22
|
+
identify missing sections
|
|
23
|
+
|
|
24
|
+
if no spec exists:
|
|
25
|
+
create one from scratch
|
|
26
|
+
this becomes the source of truth
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
check for existing documentation
|
|
30
|
+
|
|
31
|
+
<terminal>find . -type d -name "docs" 2>/dev/null</terminal>
|
|
32
|
+
<terminal>ls -la docs/ 2>/dev/null || echo "no docs directory"</terminal>
|
|
33
|
+
<terminal>find docs -name "*api*" -o -name "*endpoint*" 2>/dev/null</terminal>
|
|
34
|
+
|
|
35
|
+
read existing docs to understand:
|
|
36
|
+
- documentation structure
|
|
37
|
+
- writing style in use
|
|
38
|
+
- formatting conventions
|
|
39
|
+
- what's already documented
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
check for API source code
|
|
43
|
+
|
|
44
|
+
<terminal>find . -name "*routes*.py" -o -name "*api*.py" -o -name "*controller*.py" 2>/dev/null | head -20</terminal>
|
|
45
|
+
<terminal>find . -name "*routes*.js" -o -name "*api*.js" -o -name "*controller*.js" 2>/dev/null | head -20</terminal>
|
|
46
|
+
<terminal>find . -name "*handlers*.go" 2>/dev/null | head -10</terminal>
|
|
47
|
+
|
|
48
|
+
for each route file found:
|
|
49
|
+
<read><file>path/to/routes.py</file></read>
|
|
50
|
+
extract endpoint definitions
|
|
51
|
+
note request/response patterns
|
|
52
|
+
identify authentication requirements
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
check for authentication setup
|
|
56
|
+
|
|
57
|
+
<terminal>grep -r "jwt\|oauth\|api.*key\|bearer\|auth" --include="*.py" --include="*.js" . 2>/dev/null | head -20</terminal>
|
|
58
|
+
<terminal>find . -name "*auth*.py" -o -name "*auth*.js" 2>/dev/null | head -10</terminal>
|
|
59
|
+
|
|
60
|
+
understand:
|
|
61
|
+
- authentication method (JWT, OAuth, API Key)
|
|
62
|
+
- where credentials are passed (header, query, body)
|
|
63
|
+
- required scopes or permissions
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
check for testing files
|
|
67
|
+
|
|
68
|
+
<terminal>find . -name "*test*api*.py" -o -name "*test*route*.py" 2>/dev/null | head -10</terminal>
|
|
69
|
+
<terminal>find . -name "*test*api*.js" 2>/dev/null | head -10</terminal>
|
|
70
|
+
|
|
71
|
+
test files contain:
|
|
72
|
+
- example requests
|
|
73
|
+
- expected responses
|
|
74
|
+
- error scenarios
|
|
75
|
+
- authentication usage
|
|
76
|
+
|
|
77
|
+
these are GOLD for documentation examples.
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
PHASE 1: UNDERSTANDING YOUR API
|
|
81
|
+
|
|
82
|
+
before documenting, understand what the API does.
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
inventory all endpoints
|
|
86
|
+
|
|
87
|
+
create a complete endpoint inventory:
|
|
88
|
+
|
|
89
|
+
method path description
|
|
90
|
+
------- ------------------- ---------------------------
|
|
91
|
+
GET /api/users list all users
|
|
92
|
+
POST /api/users create new user
|
|
93
|
+
GET /api/users/{id} get user by ID
|
|
94
|
+
PUT /api/users/{id} update user
|
|
95
|
+
DELETE /api/users/{id} delete user
|
|
96
|
+
GET /api/users/{id}/posts get user's posts
|
|
97
|
+
|
|
98
|
+
use this command to find routes:
|
|
99
|
+
<terminal>grep -r "@app\|@router\|@bp\.route\|\.get\|\.post\|\.put\|\.delete" --include="*.py" . 2>/dev/null</terminal>
|
|
100
|
+
<terminal>grep -r "router\.\|app\." --include="*.js" . 2>/dev/null | grep -E "get|post|put|delete|patch" | head -30</terminal>
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
group endpoints by resource
|
|
104
|
+
|
|
105
|
+
organize into logical resource groups:
|
|
106
|
+
|
|
107
|
+
users:
|
|
108
|
+
GET /api/users
|
|
109
|
+
POST /api/users
|
|
110
|
+
GET /api/users/{id}
|
|
111
|
+
PUT /api/users/{id}
|
|
112
|
+
DELETE /api/users/{id}
|
|
113
|
+
|
|
114
|
+
posts:
|
|
115
|
+
GET /api/posts
|
|
116
|
+
POST /api/posts
|
|
117
|
+
GET /api/posts/{id}
|
|
118
|
+
PUT /api/posts/{id}
|
|
119
|
+
DELETE /api/posts/{id}
|
|
120
|
+
GET /api/posts/{id}/comments
|
|
121
|
+
|
|
122
|
+
comments:
|
|
123
|
+
POST /api/posts/{id}/comments
|
|
124
|
+
DELETE /api/comments/{id}
|
|
125
|
+
|
|
126
|
+
documentation should follow this grouping.
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
identify common patterns
|
|
130
|
+
|
|
131
|
+
look for patterns across endpoints:
|
|
132
|
+
|
|
133
|
+
[ ] pagination (page, limit, offset)
|
|
134
|
+
[ ] filtering (filter[field]=value)
|
|
135
|
+
[ ] sorting (sort=field, order=asc|desc)
|
|
136
|
+
[ ] search (q=query)
|
|
137
|
+
[ ] field selection (fields=id,name)
|
|
138
|
+
[ ] includes (include=related)
|
|
139
|
+
[ ] versioning (/v1/, /v2/)
|
|
140
|
+
[ ] rate limiting headers
|
|
141
|
+
[ ] standard response format
|
|
142
|
+
|
|
143
|
+
document patterns ONCE, then reference them.
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
PHASE 2: OPENAPI SPECIFICATION STRUCTURE
|
|
147
|
+
|
|
148
|
+
the OpenAPI specification is your foundation.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
create the base spec
|
|
152
|
+
|
|
153
|
+
<create>
|
|
154
|
+
<file>openapi.yaml</file>
|
|
155
|
+
<content>
|
|
156
|
+
openapi: 3.0.3
|
|
157
|
+
info:
|
|
158
|
+
title: My API
|
|
159
|
+
description: |
|
|
160
|
+
Detailed description of what this API does.
|
|
161
|
+
|
|
162
|
+
## Authentication
|
|
163
|
+
|
|
164
|
+
All endpoints require authentication using API keys or JWT tokens.
|
|
165
|
+
|
|
166
|
+
## Rate Limiting
|
|
167
|
+
|
|
168
|
+
Rate limits are enforced per API key.
|
|
169
|
+
version: 1.0.0
|
|
170
|
+
contact:
|
|
171
|
+
name: API Support
|
|
172
|
+
email: support@example.com
|
|
173
|
+
license:
|
|
174
|
+
name: MIT
|
|
175
|
+
url: https://opensource.org/licenses/MIT
|
|
176
|
+
|
|
177
|
+
servers:
|
|
178
|
+
- url: https://api.example.com/v1
|
|
179
|
+
description: Production server
|
|
180
|
+
- url: https://staging-api.example.com/v1
|
|
181
|
+
description: Staging server
|
|
182
|
+
- url: http://localhost:8000/v1
|
|
183
|
+
description: Local development server
|
|
184
|
+
|
|
185
|
+
tags:
|
|
186
|
+
- name: users
|
|
187
|
+
description: User management operations
|
|
188
|
+
- name: posts
|
|
189
|
+
description: Blog post operations
|
|
190
|
+
- name: auth
|
|
191
|
+
description: Authentication operations
|
|
192
|
+
|
|
193
|
+
paths:
|
|
194
|
+
# endpoints go here
|
|
195
|
+
|
|
196
|
+
components:
|
|
197
|
+
securitySchemes:
|
|
198
|
+
bearerAuth:
|
|
199
|
+
type: http
|
|
200
|
+
scheme: bearer
|
|
201
|
+
bearerFormat: JWT
|
|
202
|
+
apiKeyAuth:
|
|
203
|
+
type: apiKey
|
|
204
|
+
in: header
|
|
205
|
+
name: X-API-Key
|
|
206
|
+
|
|
207
|
+
schemas:
|
|
208
|
+
Error:
|
|
209
|
+
type: object
|
|
210
|
+
required:
|
|
211
|
+
- code
|
|
212
|
+
- message
|
|
213
|
+
properties:
|
|
214
|
+
code:
|
|
215
|
+
type: string
|
|
216
|
+
example: "validation_error"
|
|
217
|
+
message:
|
|
218
|
+
type: string
|
|
219
|
+
example: "Validation failed"
|
|
220
|
+
details:
|
|
221
|
+
type: object
|
|
222
|
+
additionalProperties: true
|
|
223
|
+
</content>
|
|
224
|
+
</create>
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
document a single endpoint fully
|
|
228
|
+
|
|
229
|
+
paths:
|
|
230
|
+
/users:
|
|
231
|
+
get:
|
|
232
|
+
summary: List all users
|
|
233
|
+
description: |
|
|
234
|
+
Returns a paginated list of users. By default, returns 20 users per page.
|
|
235
|
+
|
|
236
|
+
The response includes user profiles with basic information.
|
|
237
|
+
tags:
|
|
238
|
+
- users
|
|
239
|
+
security:
|
|
240
|
+
- bearerAuth: []
|
|
241
|
+
parameters:
|
|
242
|
+
- name: page
|
|
243
|
+
in: query
|
|
244
|
+
description: Page number for pagination
|
|
245
|
+
required: false
|
|
246
|
+
schema:
|
|
247
|
+
type: integer
|
|
248
|
+
default: 1
|
|
249
|
+
minimum: 1
|
|
250
|
+
- name: limit
|
|
251
|
+
in: query
|
|
252
|
+
description: Number of items per page
|
|
253
|
+
required: false
|
|
254
|
+
schema:
|
|
255
|
+
type: integer
|
|
256
|
+
default: 20
|
|
257
|
+
minimum: 1
|
|
258
|
+
maximum: 100
|
|
259
|
+
- name: sort
|
|
260
|
+
in: query
|
|
261
|
+
description: Sort field and order
|
|
262
|
+
required: false
|
|
263
|
+
schema:
|
|
264
|
+
type: string
|
|
265
|
+
enum: [name_asc, name_desc, created_asc, created_desc]
|
|
266
|
+
default: created_desc
|
|
267
|
+
- name: status
|
|
268
|
+
in: query
|
|
269
|
+
description: Filter by user status
|
|
270
|
+
required: false
|
|
271
|
+
schema:
|
|
272
|
+
type: string
|
|
273
|
+
enum: [active, inactive, suspended]
|
|
274
|
+
responses:
|
|
275
|
+
'200':
|
|
276
|
+
description: Successful response
|
|
277
|
+
content:
|
|
278
|
+
application/json:
|
|
279
|
+
schema:
|
|
280
|
+
type: object
|
|
281
|
+
required:
|
|
282
|
+
- data
|
|
283
|
+
- pagination
|
|
284
|
+
properties:
|
|
285
|
+
data:
|
|
286
|
+
type: array
|
|
287
|
+
items:
|
|
288
|
+
$ref: '#/components/schemas/User'
|
|
289
|
+
pagination:
|
|
290
|
+
$ref: '#/components/schemas/Pagination'
|
|
291
|
+
examples:
|
|
292
|
+
success:
|
|
293
|
+
summary: Successful response
|
|
294
|
+
value:
|
|
295
|
+
data:
|
|
296
|
+
- id: "1"
|
|
297
|
+
name: "Alice Johnson"
|
|
298
|
+
email: "alice@example.com"
|
|
299
|
+
status: "active"
|
|
300
|
+
created_at: "2024-01-15T10:30:00Z"
|
|
301
|
+
pagination:
|
|
302
|
+
page: 1
|
|
303
|
+
limit: 20
|
|
304
|
+
total: 145
|
|
305
|
+
pages: 8
|
|
306
|
+
'400':
|
|
307
|
+
description: Bad request - invalid parameters
|
|
308
|
+
content:
|
|
309
|
+
application/json:
|
|
310
|
+
schema:
|
|
311
|
+
$ref: '#/components/schemas/Error'
|
|
312
|
+
example:
|
|
313
|
+
code: "validation_error"
|
|
314
|
+
message: "Invalid sort parameter"
|
|
315
|
+
details:
|
|
316
|
+
field: "sort"
|
|
317
|
+
errors: ["Must be one of: name_asc, name_desc"]
|
|
318
|
+
'401':
|
|
319
|
+
description: Unauthorized - missing or invalid token
|
|
320
|
+
content:
|
|
321
|
+
application/json:
|
|
322
|
+
schema:
|
|
323
|
+
$ref: '#/components/schemas/Error'
|
|
324
|
+
example:
|
|
325
|
+
code: "unauthorized"
|
|
326
|
+
message: "Authentication required"
|
|
327
|
+
'429':
|
|
328
|
+
description: Too many requests
|
|
329
|
+
content:
|
|
330
|
+
application/json:
|
|
331
|
+
schema:
|
|
332
|
+
$ref: '#/components/schemas/Error'
|
|
333
|
+
example:
|
|
334
|
+
code: "rate_limit_exceeded"
|
|
335
|
+
message: "Rate limit exceeded. Try again in 1 minute."
|
|
336
|
+
headers:
|
|
337
|
+
X-RateLimit-Limit:
|
|
338
|
+
schema:
|
|
339
|
+
type: integer
|
|
340
|
+
description: Request limit per time window
|
|
341
|
+
X-RateLimit-Remaining:
|
|
342
|
+
schema:
|
|
343
|
+
type: integer
|
|
344
|
+
description: Requests remaining in window
|
|
345
|
+
X-RateLimit-Reset:
|
|
346
|
+
schema:
|
|
347
|
+
type: integer
|
|
348
|
+
description: Unix timestamp when limit resets
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
PHASE 3: REQUEST DOCUMENTATION
|
|
352
|
+
|
|
353
|
+
every possible input must be documented.
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
path parameters
|
|
357
|
+
|
|
358
|
+
parameters:
|
|
359
|
+
- name: user_id
|
|
360
|
+
in: path
|
|
361
|
+
description: |
|
|
362
|
+
The unique identifier of the user.
|
|
363
|
+
|
|
364
|
+
This can be either:
|
|
365
|
+
- The numeric user ID
|
|
366
|
+
- The string "me" for the authenticated user
|
|
367
|
+
|
|
368
|
+
Example: `/users/123` or `/users/me`
|
|
369
|
+
required: true
|
|
370
|
+
schema:
|
|
371
|
+
type: string
|
|
372
|
+
pattern: '^[a-z0-9_-]+$'
|
|
373
|
+
example: "123"
|
|
374
|
+
|
|
375
|
+
always document:
|
|
376
|
+
[ ] what the parameter represents
|
|
377
|
+
[ ] valid values or patterns
|
|
378
|
+
[ ] if it's optional (should not be for path params)
|
|
379
|
+
[ ] example values
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
query parameters
|
|
383
|
+
|
|
384
|
+
parameters:
|
|
385
|
+
- name: include
|
|
386
|
+
in: query
|
|
387
|
+
description: |
|
|
388
|
+
Related resources to include in the response.
|
|
389
|
+
|
|
390
|
+
Multiple values can be comma-separated.
|
|
391
|
+
|
|
392
|
+
Available includes:
|
|
393
|
+
- `profile` - user profile information
|
|
394
|
+
- `settings` - user preferences
|
|
395
|
+
- `stats` - user statistics
|
|
396
|
+
|
|
397
|
+
Example: `?include=profile,stats`
|
|
398
|
+
required: false
|
|
399
|
+
schema:
|
|
400
|
+
type: string
|
|
401
|
+
example: "profile,stats"
|
|
402
|
+
|
|
403
|
+
- name: fields
|
|
404
|
+
in: query
|
|
405
|
+
description: |
|
|
406
|
+
Comma-separated list of fields to return.
|
|
407
|
+
|
|
408
|
+
Use this to reduce response size by requesting only needed fields.
|
|
409
|
+
|
|
410
|
+
Example: `?fields=id,name,email`
|
|
411
|
+
required: false
|
|
412
|
+
schema:
|
|
413
|
+
type: string
|
|
414
|
+
example: "id,name,email"
|
|
415
|
+
|
|
416
|
+
- name: filter
|
|
417
|
+
in: query
|
|
418
|
+
description: |
|
|
419
|
+
Filter results by field values.
|
|
420
|
+
|
|
421
|
+
Syntax: `filter[field]=value`
|
|
422
|
+
|
|
423
|
+
Multiple filters can be combined.
|
|
424
|
+
|
|
425
|
+
Supported operators:
|
|
426
|
+
- `filter[name]=Alice` - exact match
|
|
427
|
+
- `filter[name][contains]=Ali` - contains
|
|
428
|
+
- `filter[name][starts]=Al` - starts with
|
|
429
|
+
- `filter[age][gte]=18` - greater than or equal
|
|
430
|
+
- `filter[age][lte]=65` - less than or equal
|
|
431
|
+
|
|
432
|
+
Example: `?filter[status]=active&filter[age][gte]=18`
|
|
433
|
+
required: false
|
|
434
|
+
schema:
|
|
435
|
+
type: object
|
|
436
|
+
additionalProperties: true
|
|
437
|
+
style: deepObject
|
|
438
|
+
explode: true
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
request headers
|
|
442
|
+
|
|
443
|
+
document all relevant headers:
|
|
444
|
+
|
|
445
|
+
parameters:
|
|
446
|
+
- name: Accept
|
|
447
|
+
in: header
|
|
448
|
+
description: |
|
|
449
|
+
Response content format.
|
|
450
|
+
|
|
451
|
+
Supported values:
|
|
452
|
+
- `application/json` (default)
|
|
453
|
+
- `application/vnd.api+json` (JSON:API format)
|
|
454
|
+
required: false
|
|
455
|
+
schema:
|
|
456
|
+
type: string
|
|
457
|
+
enum: [application/json, application/vnd.api+json]
|
|
458
|
+
default: application/json
|
|
459
|
+
|
|
460
|
+
- name: Accept-Language
|
|
461
|
+
in: header
|
|
462
|
+
description: |
|
|
463
|
+
Preferred language for response messages.
|
|
464
|
+
|
|
465
|
+
Supported: `en`, `es`, `fr`, `de`
|
|
466
|
+
required: false
|
|
467
|
+
schema:
|
|
468
|
+
type: string
|
|
469
|
+
example: "en"
|
|
470
|
+
|
|
471
|
+
- name: Idempotency-Key
|
|
472
|
+
in: header
|
|
473
|
+
description: |
|
|
474
|
+
A unique key to ensure idempotent requests.
|
|
475
|
+
|
|
476
|
+
Use this for POST/PUT operations that should only execute once.
|
|
477
|
+
The key should be a UUID or unique identifier.
|
|
478
|
+
|
|
479
|
+
The key expires after 24 hours.
|
|
480
|
+
required: false
|
|
481
|
+
schema:
|
|
482
|
+
type: string
|
|
483
|
+
format: uuid
|
|
484
|
+
example: "550e8400-e29b-41d4-a716-446655440000"
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
request body
|
|
488
|
+
|
|
489
|
+
requestBodies:
|
|
490
|
+
createUser:
|
|
491
|
+
description: User object to create
|
|
492
|
+
required: true
|
|
493
|
+
content:
|
|
494
|
+
application/json:
|
|
495
|
+
schema:
|
|
496
|
+
$ref: '#/components/schemas/CreateUserRequest'
|
|
497
|
+
examples:
|
|
498
|
+
minimal:
|
|
499
|
+
summary: Minimal user creation
|
|
500
|
+
value:
|
|
501
|
+
email: "user@example.com"
|
|
502
|
+
password: "SecurePassword123!"
|
|
503
|
+
full:
|
|
504
|
+
summary: Complete user creation with all fields
|
|
505
|
+
value:
|
|
506
|
+
email: "alice@example.com"
|
|
507
|
+
password: "SecurePassword123!"
|
|
508
|
+
name: "Alice Johnson"
|
|
509
|
+
phone: "+1-555-0123"
|
|
510
|
+
timezone: "America/New_York"
|
|
511
|
+
locale: "en"
|
|
512
|
+
withProfile:
|
|
513
|
+
summary: User with profile information
|
|
514
|
+
value:
|
|
515
|
+
email: "bob@example.com"
|
|
516
|
+
password: "SecurePassword123!"
|
|
517
|
+
name: "Bob Smith"
|
|
518
|
+
profile:
|
|
519
|
+
bio: "Software developer"
|
|
520
|
+
location: "San Francisco, CA"
|
|
521
|
+
website: "https://bob.example.com"
|
|
522
|
+
application/x-www-form-urlencoded:
|
|
523
|
+
schema:
|
|
524
|
+
type: object
|
|
525
|
+
required:
|
|
526
|
+
- email
|
|
527
|
+
- password
|
|
528
|
+
properties:
|
|
529
|
+
email:
|
|
530
|
+
type: string
|
|
531
|
+
format: email
|
|
532
|
+
password:
|
|
533
|
+
type: string
|
|
534
|
+
minLength: 8
|
|
535
|
+
name:
|
|
536
|
+
type: string
|
|
537
|
+
encoding:
|
|
538
|
+
profile:
|
|
539
|
+
style: form
|
|
540
|
+
explode: false
|
|
541
|
+
|
|
542
|
+
components:
|
|
543
|
+
schemas:
|
|
544
|
+
CreateUserRequest:
|
|
545
|
+
type: object
|
|
546
|
+
required:
|
|
547
|
+
- email
|
|
548
|
+
- password
|
|
549
|
+
properties:
|
|
550
|
+
email:
|
|
551
|
+
type: string
|
|
552
|
+
format: email
|
|
553
|
+
description: |
|
|
554
|
+
User's email address. Must be unique across all users.
|
|
555
|
+
|
|
556
|
+
A confirmation email will be sent to this address.
|
|
557
|
+
example: "user@example.com"
|
|
558
|
+
password:
|
|
559
|
+
type: string
|
|
560
|
+
minLength: 8
|
|
561
|
+
maxLength: 128
|
|
562
|
+
description: |
|
|
563
|
+
User's password. Must be at least 8 characters.
|
|
564
|
+
|
|
565
|
+
Requirements:
|
|
566
|
+
- At least 8 characters
|
|
567
|
+
- At least one uppercase letter
|
|
568
|
+
- At least one number
|
|
569
|
+
- At least one special character
|
|
570
|
+
example: "SecurePass123!"
|
|
571
|
+
name:
|
|
572
|
+
type: string
|
|
573
|
+
maxLength: 100
|
|
574
|
+
description: User's display name
|
|
575
|
+
example: "Alice Johnson"
|
|
576
|
+
phone:
|
|
577
|
+
type: string
|
|
578
|
+
pattern: '^\+?[1-9]\d{1,14}$'
|
|
579
|
+
description: |
|
|
580
|
+
Phone number in E.164 format.
|
|
581
|
+
|
|
582
|
+
Include country code prefixed with +.
|
|
583
|
+
example: "+1-555-0123"
|
|
584
|
+
timezone:
|
|
585
|
+
type: string
|
|
586
|
+
description: User's timezone for display purposes
|
|
587
|
+
example: "America/New_York"
|
|
588
|
+
locale:
|
|
589
|
+
type: string
|
|
590
|
+
description: Preferred language/locale
|
|
591
|
+
example: "en"
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
PHASE 4: RESPONSE DOCUMENTATION
|
|
595
|
+
|
|
596
|
+
document all possible responses.
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
success response structure
|
|
600
|
+
|
|
601
|
+
responses:
|
|
602
|
+
'200':
|
|
603
|
+
description: Successful operation
|
|
604
|
+
content:
|
|
605
|
+
application/json:
|
|
606
|
+
schema:
|
|
607
|
+
allOf:
|
|
608
|
+
- $ref: '#/components/schemas/SuccessResponse'
|
|
609
|
+
- type: object
|
|
610
|
+
properties:
|
|
611
|
+
data:
|
|
612
|
+
$ref: '#/components/schemas/User'
|
|
613
|
+
|
|
614
|
+
components:
|
|
615
|
+
schemas:
|
|
616
|
+
SuccessResponse:
|
|
617
|
+
type: object
|
|
618
|
+
required:
|
|
619
|
+
- data
|
|
620
|
+
- meta
|
|
621
|
+
properties:
|
|
622
|
+
data:
|
|
623
|
+
description: The primary response data
|
|
624
|
+
meta:
|
|
625
|
+
type: object
|
|
626
|
+
description: Metadata about the response
|
|
627
|
+
properties:
|
|
628
|
+
id:
|
|
629
|
+
type: string
|
|
630
|
+
description: Unique request ID for tracing
|
|
631
|
+
timestamp:
|
|
632
|
+
type: string
|
|
633
|
+
format: date-time
|
|
634
|
+
description: When the response was generated
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
pagination in responses
|
|
638
|
+
|
|
639
|
+
components:
|
|
640
|
+
schemas:
|
|
641
|
+
PaginatedResponse:
|
|
642
|
+
type: object
|
|
643
|
+
required:
|
|
644
|
+
- data
|
|
645
|
+
- pagination
|
|
646
|
+
properties:
|
|
647
|
+
data:
|
|
648
|
+
type: array
|
|
649
|
+
description: Array of items
|
|
650
|
+
pagination:
|
|
651
|
+
type: object
|
|
652
|
+
required:
|
|
653
|
+
- page
|
|
654
|
+
- limit
|
|
655
|
+
- total
|
|
656
|
+
- pages
|
|
657
|
+
properties:
|
|
658
|
+
page:
|
|
659
|
+
type: integer
|
|
660
|
+
minimum: 1
|
|
661
|
+
description: Current page number
|
|
662
|
+
example: 1
|
|
663
|
+
limit:
|
|
664
|
+
type: integer
|
|
665
|
+
minimum: 1
|
|
666
|
+
maximum: 100
|
|
667
|
+
description: Items per page
|
|
668
|
+
example: 20
|
|
669
|
+
total:
|
|
670
|
+
type: integer
|
|
671
|
+
minimum: 0
|
|
672
|
+
description: Total number of items
|
|
673
|
+
example: 145
|
|
674
|
+
pages:
|
|
675
|
+
type: integer
|
|
676
|
+
minimum: 1
|
|
677
|
+
description: Total number of pages
|
|
678
|
+
example: 8
|
|
679
|
+
has_prev:
|
|
680
|
+
type: boolean
|
|
681
|
+
description: Whether there is a previous page
|
|
682
|
+
has_next:
|
|
683
|
+
type: boolean
|
|
684
|
+
description: Whether there is a next page
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
resource schemas
|
|
688
|
+
|
|
689
|
+
components:
|
|
690
|
+
schemas:
|
|
691
|
+
User:
|
|
692
|
+
type: object
|
|
693
|
+
required:
|
|
694
|
+
- id
|
|
695
|
+
- email
|
|
696
|
+
- created_at
|
|
697
|
+
properties:
|
|
698
|
+
id:
|
|
699
|
+
type: string
|
|
700
|
+
description: Unique user identifier
|
|
701
|
+
example: "usr_abc123xyz"
|
|
702
|
+
email:
|
|
703
|
+
type: string
|
|
704
|
+
format: email
|
|
705
|
+
description: User's email address
|
|
706
|
+
example: "alice@example.com"
|
|
707
|
+
name:
|
|
708
|
+
type: string
|
|
709
|
+
nullable: true
|
|
710
|
+
description: User's display name
|
|
711
|
+
example: "Alice Johnson"
|
|
712
|
+
avatar_url:
|
|
713
|
+
type: string
|
|
714
|
+
format: uri
|
|
715
|
+
nullable: true
|
|
716
|
+
description: URL to user's avatar image
|
|
717
|
+
example: "https://cdn.example.com/avatars/usr_abc123xyz.jpg"
|
|
718
|
+
status:
|
|
719
|
+
type: string
|
|
720
|
+
enum: [active, inactive, suspended, deleted]
|
|
721
|
+
description: Current user status
|
|
722
|
+
example: "active"
|
|
723
|
+
role:
|
|
724
|
+
type: string
|
|
725
|
+
enum: [user, admin, moderator]
|
|
726
|
+
description: User's role in the system
|
|
727
|
+
example: "user"
|
|
728
|
+
created_at:
|
|
729
|
+
type: string
|
|
730
|
+
format: date-time
|
|
731
|
+
description: When the user account was created
|
|
732
|
+
example: "2024-01-15T10:30:00Z"
|
|
733
|
+
updated_at:
|
|
734
|
+
type: string
|
|
735
|
+
format: date-time
|
|
736
|
+
nullable: true
|
|
737
|
+
description: When the user was last updated
|
|
738
|
+
example: "2024-01-20T14:22:00Z"
|
|
739
|
+
last_login_at:
|
|
740
|
+
type: string
|
|
741
|
+
format: date-time
|
|
742
|
+
nullable: true
|
|
743
|
+
description: Last time user logged in
|
|
744
|
+
example: "2024-01-25T09:15:00Z"
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
error responses
|
|
748
|
+
|
|
749
|
+
create standard error response types:
|
|
750
|
+
|
|
751
|
+
components:
|
|
752
|
+
schemas:
|
|
753
|
+
Error:
|
|
754
|
+
type: object
|
|
755
|
+
required:
|
|
756
|
+
- error
|
|
757
|
+
properties:
|
|
758
|
+
error:
|
|
759
|
+
type: object
|
|
760
|
+
required:
|
|
761
|
+
- code
|
|
762
|
+
- message
|
|
763
|
+
properties:
|
|
764
|
+
code:
|
|
765
|
+
type: string
|
|
766
|
+
description: Machine-readable error code
|
|
767
|
+
example: "validation_error"
|
|
768
|
+
message:
|
|
769
|
+
type: string
|
|
770
|
+
description: Human-readable error message
|
|
771
|
+
example: "The request failed validation"
|
|
772
|
+
details:
|
|
773
|
+
oneOf:
|
|
774
|
+
- type: object
|
|
775
|
+
additionalProperties: true
|
|
776
|
+
description: Additional error details
|
|
777
|
+
- type: array
|
|
778
|
+
items:
|
|
779
|
+
type: object
|
|
780
|
+
properties:
|
|
781
|
+
field:
|
|
782
|
+
type: string
|
|
783
|
+
description: Field name with error
|
|
784
|
+
message:
|
|
785
|
+
type: string
|
|
786
|
+
description: Error message for this field
|
|
787
|
+
code:
|
|
788
|
+
type: string
|
|
789
|
+
description: Error code for this field
|
|
790
|
+
stacktrace:
|
|
791
|
+
type: string
|
|
792
|
+
description: Stack trace (development only)
|
|
793
|
+
deprecated: true
|
|
794
|
+
request_id:
|
|
795
|
+
type: string
|
|
796
|
+
description: Request ID for support
|
|
797
|
+
example: "req_abc123xyz"
|
|
798
|
+
|
|
799
|
+
common error codes to document:
|
|
800
|
+
|
|
801
|
+
400 Bad Request
|
|
802
|
+
- validation_error: Invalid input data
|
|
803
|
+
- invalid_json: Malformed JSON
|
|
804
|
+
- missing_required_field: Required field missing
|
|
805
|
+
- invalid_format: Field format invalid
|
|
806
|
+
|
|
807
|
+
401 Unauthorized
|
|
808
|
+
- unauthorized: No authentication provided
|
|
809
|
+
- invalid_token: Token is invalid or expired
|
|
810
|
+
- insufficient_permissions: Lacks required scope/role
|
|
811
|
+
|
|
812
|
+
403 Forbidden
|
|
813
|
+
- access_denied: Access to resource denied
|
|
814
|
+
- account_suspended: Account is suspended
|
|
815
|
+
- quota_exceeded: API quota exceeded
|
|
816
|
+
|
|
817
|
+
404 Not Found
|
|
818
|
+
- not_found: Resource does not exist
|
|
819
|
+
|
|
820
|
+
409 Conflict
|
|
821
|
+
- duplicate: Resource already exists
|
|
822
|
+
- conflict: State conflict
|
|
823
|
+
|
|
824
|
+
422 Unprocessable Entity
|
|
825
|
+
- semantic_error: Request is valid but cannot be processed
|
|
826
|
+
|
|
827
|
+
429 Too Many Requests
|
|
828
|
+
- rate_limit_exceeded: Rate limit exceeded
|
|
829
|
+
- temporarily_unavailable: Service temporarily unavailable
|
|
830
|
+
|
|
831
|
+
500 Internal Server Error
|
|
832
|
+
- internal_error: Unexpected server error
|
|
833
|
+
|
|
834
|
+
503 Service Unavailable
|
|
835
|
+
- service_unavailable: Service is down for maintenance
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
PHASE 5: AUTHENTICATION DOCUMENTATION
|
|
839
|
+
|
|
840
|
+
clearly explain how to authenticate.
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
authentication overview section
|
|
844
|
+
|
|
845
|
+
create a dedicated authentication guide:
|
|
846
|
+
|
|
847
|
+
## Authentication
|
|
848
|
+
|
|
849
|
+
All API requests require authentication. We support two methods:
|
|
850
|
+
|
|
851
|
+
### Bearer Token (JWT)
|
|
852
|
+
|
|
853
|
+
Most common method. Include your JWT token in the Authorization header.
|
|
854
|
+
|
|
855
|
+
#### Getting a Token
|
|
856
|
+
|
|
857
|
+
First, authenticate to receive a token:
|
|
858
|
+
|
|
859
|
+
```bash
|
|
860
|
+
curl -X POST https://api.example.com/v1/auth/token \
|
|
861
|
+
-H "Content-Type: application/json" \
|
|
862
|
+
-d '{
|
|
863
|
+
"client_id": "your_client_id",
|
|
864
|
+
"client_secret": "your_client_secret",
|
|
865
|
+
"grant_type": "client_credentials"
|
|
866
|
+
}'
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
Response:
|
|
870
|
+
```json
|
|
871
|
+
{
|
|
872
|
+
"access_token": "eyJhbGciOiJIUzI1NiIs...",
|
|
873
|
+
"token_type": "Bearer",
|
|
874
|
+
"expires_in": 3600,
|
|
875
|
+
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
|
|
876
|
+
"scope": "read write"
|
|
877
|
+
}
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
#### Using the Token
|
|
881
|
+
|
|
882
|
+
Include the token in subsequent requests:
|
|
883
|
+
|
|
884
|
+
```bash
|
|
885
|
+
curl https://api.example.com/v1/users \
|
|
886
|
+
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
#### Token Expiration
|
|
890
|
+
|
|
891
|
+
Access tokens expire after 1 hour. Use the refresh token to get a new access token:
|
|
892
|
+
|
|
893
|
+
```bash
|
|
894
|
+
curl -X POST https://api.example.com/v1/auth/refresh \
|
|
895
|
+
-H "Content-Type: application/json" \
|
|
896
|
+
-d '{
|
|
897
|
+
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
|
|
898
|
+
}'
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### API Key
|
|
902
|
+
|
|
903
|
+
For simple integrations, use an API key.
|
|
904
|
+
|
|
905
|
+
#### Getting an API Key
|
|
906
|
+
|
|
907
|
+
Generate an API key from your dashboard:
|
|
908
|
+
|
|
909
|
+
1. Go to Settings > API Keys
|
|
910
|
+
2. Click "Generate API Key"
|
|
911
|
+
3. Copy the key (you won't see it again)
|
|
912
|
+
|
|
913
|
+
#### Using the API Key
|
|
914
|
+
|
|
915
|
+
Include the key in the X-API-Key header:
|
|
916
|
+
|
|
917
|
+
```bash
|
|
918
|
+
curl https://api.example.com/v1/users \
|
|
919
|
+
-H "X-API-Key: pk_live_abc123xyz..."
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
Or as a query parameter (not recommended for production):
|
|
923
|
+
|
|
924
|
+
```bash
|
|
925
|
+
curl "https://api.example.com/v1/users?api_key=pk_live_abc123xyz..."
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
document scopes and permissions
|
|
930
|
+
|
|
931
|
+
### Scopes
|
|
932
|
+
|
|
933
|
+
Access tokens can include scopes for granular permissions:
|
|
934
|
+
|
|
935
|
+
| Scope | Description | Example Endpoints |
|
|
936
|
+
|-------|-------------|-------------------|
|
|
937
|
+
| `users:read` | Read user information | GET /users, GET /users/{id} |
|
|
938
|
+
| `users:write` | Create and modify users | POST /users, PUT /users/{id} |
|
|
939
|
+
| `users:delete` | Delete users | DELETE /users/{id} |
|
|
940
|
+
| `posts:read` | Read posts | GET /posts |
|
|
941
|
+
| `posts:write` | Create and modify posts | POST /posts |
|
|
942
|
+
| `admin` | Full administrative access | All endpoints |
|
|
943
|
+
|
|
944
|
+
#### Requesting Scopes
|
|
945
|
+
|
|
946
|
+
Include scopes in your authentication request:
|
|
947
|
+
|
|
948
|
+
```bash
|
|
949
|
+
curl -X POST https://api.example.com/v1/auth/token \
|
|
950
|
+
-H "Content-Type: application/json" \
|
|
951
|
+
-d '{
|
|
952
|
+
"client_id": "your_client_id",
|
|
953
|
+
"client_secret": "your_client_secret",
|
|
954
|
+
"grant_type": "client_credentials",
|
|
955
|
+
"scope": "users:read posts:write"
|
|
956
|
+
}'
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
PHASE 6: INTERACTIVE DOCUMENTATION
|
|
961
|
+
|
|
962
|
+
make your API documentation interactive.
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
swagger UI setup
|
|
966
|
+
|
|
967
|
+
add Swagger UI to display interactive docs:
|
|
968
|
+
|
|
969
|
+
<create>
|
|
970
|
+
<file>docs/swagger.html</file>
|
|
971
|
+
<content>
|
|
972
|
+
<!DOCTYPE html>
|
|
973
|
+
<html>
|
|
974
|
+
<head>
|
|
975
|
+
<link rel="stylesheet" type="text/css"
|
|
976
|
+
href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css">
|
|
977
|
+
<style>
|
|
978
|
+
body { margin: 0; padding: 0; }
|
|
979
|
+
#swagger-ui { max-width: 1460px; margin: 0 auto; }
|
|
980
|
+
</style>
|
|
981
|
+
</head>
|
|
982
|
+
<body>
|
|
983
|
+
<div id="swagger-ui"></div>
|
|
984
|
+
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
|
985
|
+
<script>
|
|
986
|
+
window.onload = function() {
|
|
987
|
+
SwaggerUIBundle({
|
|
988
|
+
url: "/openapi.yaml",
|
|
989
|
+
dom_id: '#swagger-ui',
|
|
990
|
+
deepLinking: true,
|
|
991
|
+
presets: [
|
|
992
|
+
SwaggerUIBundle.presets.apis,
|
|
993
|
+
SwaggerUIBundle.SwaggerUIStandalonePreset
|
|
994
|
+
],
|
|
995
|
+
plugins: [
|
|
996
|
+
SwaggerUIBundle.plugins.DownloadUrl
|
|
997
|
+
],
|
|
998
|
+
layout: "BaseLayout",
|
|
999
|
+
defaultModelsExpandDepth: 1,
|
|
1000
|
+
defaultModelExpandDepth: 1,
|
|
1001
|
+
docExpansion: "list",
|
|
1002
|
+
filter: true,
|
|
1003
|
+
tryItOutEnabled: true,
|
|
1004
|
+
persistAuthorization: true,
|
|
1005
|
+
requestInterceptor: (request) => {
|
|
1006
|
+
// Add default headers if needed
|
|
1007
|
+
return request;
|
|
1008
|
+
},
|
|
1009
|
+
responseInterceptor: (response) => {
|
|
1010
|
+
// Log responses if needed
|
|
1011
|
+
return response;
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
</script>
|
|
1016
|
+
</body>
|
|
1017
|
+
</html>
|
|
1018
|
+
</content>
|
|
1019
|
+
</create>
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
redoc setup
|
|
1023
|
+
|
|
1024
|
+
for an alternative clean documentation style:
|
|
1025
|
+
|
|
1026
|
+
<create>
|
|
1027
|
+
<file>docs/redoc.html</file>
|
|
1028
|
+
<content>
|
|
1029
|
+
<!DOCTYPE html>
|
|
1030
|
+
<html>
|
|
1031
|
+
<head>
|
|
1032
|
+
<title>API Reference</title>
|
|
1033
|
+
<meta charset="utf-8"/>
|
|
1034
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1035
|
+
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
|
1036
|
+
<style>
|
|
1037
|
+
body { margin: 0; padding: 0; }
|
|
1038
|
+
</style>
|
|
1039
|
+
<link rel="stylesheet" type="text/css"
|
|
1040
|
+
href="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.min.css">
|
|
1041
|
+
</head>
|
|
1042
|
+
<body>
|
|
1043
|
+
<redoc spec-url="/openapi.yaml"></redoc>
|
|
1044
|
+
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"></script>
|
|
1045
|
+
</body>
|
|
1046
|
+
</html>
|
|
1047
|
+
</content>
|
|
1048
|
+
</create>
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
PHASE 7: CODE EXAMPLES
|
|
1052
|
+
|
|
1053
|
+
provide working examples in multiple languages.
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
curl examples
|
|
1057
|
+
|
|
1058
|
+
```bash
|
|
1059
|
+
# List all users
|
|
1060
|
+
curl "https://api.example.com/v1/users?page=1&limit=20" \
|
|
1061
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
1062
|
+
-H "Accept: application/json"
|
|
1063
|
+
|
|
1064
|
+
# Create a new user
|
|
1065
|
+
curl -X POST "https://api.example.com/v1/users" \
|
|
1066
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
1067
|
+
-H "Content-Type: application/json" \
|
|
1068
|
+
-H "Idempotency-Key: $(uuidgen)" \
|
|
1069
|
+
-d '{
|
|
1070
|
+
"email": "newuser@example.com",
|
|
1071
|
+
"password": "SecurePassword123!",
|
|
1072
|
+
"name": "New User"
|
|
1073
|
+
}'
|
|
1074
|
+
|
|
1075
|
+
# Update a user
|
|
1076
|
+
curl -X PUT "https://api.example.com/v1/users/usr_abc123" \
|
|
1077
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
1078
|
+
-H "Content-Type: application/json" \
|
|
1079
|
+
-d '{
|
|
1080
|
+
"name": "Updated Name"
|
|
1081
|
+
}'
|
|
1082
|
+
|
|
1083
|
+
# Delete a user
|
|
1084
|
+
curl -X DELETE "https://api.example.com/v1/users/usr_abc123" \
|
|
1085
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
python examples
|
|
1090
|
+
|
|
1091
|
+
```python
|
|
1092
|
+
import requests
|
|
1093
|
+
import json
|
|
1094
|
+
|
|
1095
|
+
# Configure
|
|
1096
|
+
base_url = "https://api.example.com/v1"
|
|
1097
|
+
token = "YOUR_TOKEN"
|
|
1098
|
+
|
|
1099
|
+
headers = {
|
|
1100
|
+
"Authorization": f"Bearer {token}",
|
|
1101
|
+
"Content-Type": "application/json",
|
|
1102
|
+
"Accept": "application/json"
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
# List users
|
|
1106
|
+
response = requests.get(
|
|
1107
|
+
f"{base_url}/users",
|
|
1108
|
+
headers=headers,
|
|
1109
|
+
params={"page": 1, "limit": 20}
|
|
1110
|
+
)
|
|
1111
|
+
response.raise_for_status()
|
|
1112
|
+
users = response.json()["data"]
|
|
1113
|
+
print(f"Found {len(users)} users")
|
|
1114
|
+
|
|
1115
|
+
# Create user
|
|
1116
|
+
new_user = {
|
|
1117
|
+
"email": "newuser@example.com",
|
|
1118
|
+
"password": "SecurePassword123!",
|
|
1119
|
+
"name": "New User"
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
response = requests.post(
|
|
1123
|
+
f"{base_url}/users",
|
|
1124
|
+
headers=headers,
|
|
1125
|
+
json=new_user
|
|
1126
|
+
)
|
|
1127
|
+
response.raise_for_status()
|
|
1128
|
+
user = response.json()["data"]
|
|
1129
|
+
print(f"Created user: {user['id']}")
|
|
1130
|
+
|
|
1131
|
+
# Upload file
|
|
1132
|
+
with open("avatar.jpg", "rb") as f:
|
|
1133
|
+
response = requests.put(
|
|
1134
|
+
f"{base_url}/users/{user['id']}/avatar",
|
|
1135
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
1136
|
+
files={"file": f}
|
|
1137
|
+
)
|
|
1138
|
+
response.raise_for_status()
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
javascript examples
|
|
1143
|
+
|
|
1144
|
+
```javascript
|
|
1145
|
+
// Using fetch API
|
|
1146
|
+
const baseUrl = "https://api.example.com/v1";
|
|
1147
|
+
const token = "YOUR_TOKEN";
|
|
1148
|
+
|
|
1149
|
+
const headers = {
|
|
1150
|
+
"Authorization": `Bearer ${token}`,
|
|
1151
|
+
"Content-Type": "application/json",
|
|
1152
|
+
"Accept": "application/json"
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
// List users
|
|
1156
|
+
async function listUsers(page = 1) {
|
|
1157
|
+
const response = await fetch(
|
|
1158
|
+
`${baseUrl}/users?page=${page}&limit=20`,
|
|
1159
|
+
{ headers }
|
|
1160
|
+
);
|
|
1161
|
+
|
|
1162
|
+
if (!response.ok) {
|
|
1163
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
const data = await response.json();
|
|
1167
|
+
return data.data;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Create user
|
|
1171
|
+
async function createUser(userData) {
|
|
1172
|
+
const response = await fetch(`${baseUrl}/users`, {
|
|
1173
|
+
method: "POST",
|
|
1174
|
+
headers: {
|
|
1175
|
+
...headers,
|
|
1176
|
+
"Idempotency-Key": crypto.randomUUID()
|
|
1177
|
+
},
|
|
1178
|
+
body: JSON.stringify(userData)
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
if (!response.ok) {
|
|
1182
|
+
const error = await response.json();
|
|
1183
|
+
throw new Error(error.error.message);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const data = await response.json();
|
|
1187
|
+
return data.data;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// Usage
|
|
1191
|
+
try {
|
|
1192
|
+
const users = await listUsers();
|
|
1193
|
+
console.log(`Found ${users.length} users`);
|
|
1194
|
+
|
|
1195
|
+
const newUser = await createUser({
|
|
1196
|
+
email: "newuser@example.com",
|
|
1197
|
+
password: "SecurePassword123!",
|
|
1198
|
+
name: "New User"
|
|
1199
|
+
});
|
|
1200
|
+
console.log(`Created: ${newUser.id}`);
|
|
1201
|
+
} catch (error) {
|
|
1202
|
+
console.error("Error:", error.message);
|
|
1203
|
+
}
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
node.js with axios
|
|
1208
|
+
|
|
1209
|
+
```javascript
|
|
1210
|
+
const axios = require('axios');
|
|
1211
|
+
|
|
1212
|
+
// Configure client
|
|
1213
|
+
const api = axios.create({
|
|
1214
|
+
baseURL: 'https://api.example.com/v1',
|
|
1215
|
+
headers: {
|
|
1216
|
+
'Authorization': `Bearer ${process.env.API_TOKEN}`,
|
|
1217
|
+
'Content-Type': 'application/json'
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
// List users
|
|
1222
|
+
async function listUsers() {
|
|
1223
|
+
try {
|
|
1224
|
+
const response = await api.get('/users', {
|
|
1225
|
+
params: { page: 1, limit: 20 }
|
|
1226
|
+
});
|
|
1227
|
+
return response.data.data;
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
if (error.response) {
|
|
1230
|
+
throw new Error(error.response.data.error.message);
|
|
1231
|
+
}
|
|
1232
|
+
throw error;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// Create user with automatic retry
|
|
1237
|
+
async function createUser(userData, maxRetries = 3) {
|
|
1238
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1239
|
+
try {
|
|
1240
|
+
const response = await api.post('/users', userData, {
|
|
1241
|
+
headers: {
|
|
1242
|
+
'Idempotency-Key': require('crypto').randomUUID()
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
return response.data.data;
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
if (error.response?.status === 429 && attempt < maxRetries - 1) {
|
|
1248
|
+
// Rate limited - wait and retry
|
|
1249
|
+
const retryAfter = error.response.headers['retry-after'];
|
|
1250
|
+
await new Promise(r => setTimeout(r, (retryAfter || 1) * 1000));
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
throw error;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
go examples
|
|
1261
|
+
|
|
1262
|
+
```go
|
|
1263
|
+
package main
|
|
1264
|
+
|
|
1265
|
+
import (
|
|
1266
|
+
"bytes"
|
|
1267
|
+
"encoding/json"
|
|
1268
|
+
"fmt"
|
|
1269
|
+
"io"
|
|
1270
|
+
"net/http"
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
const (
|
|
1274
|
+
BaseURL = "https://api.example.com/v1"
|
|
1275
|
+
Token = "YOUR_TOKEN"
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
type Client struct {
|
|
1279
|
+
HTTPClient *http.Client
|
|
1280
|
+
BaseURL string
|
|
1281
|
+
Token string
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
func NewClient(token string) *Client {
|
|
1285
|
+
return &Client{
|
|
1286
|
+
HTTPClient: &http.Client{},
|
|
1287
|
+
BaseURL: BaseURL,
|
|
1288
|
+
Token: token,
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
func (c *Client) doRequest(method, path string, body io.Reader) (*http.Response, error) {
|
|
1293
|
+
req, err := http.NewRequest(method, c.BaseURL+path, body)
|
|
1294
|
+
if err != nil {
|
|
1295
|
+
return nil, err
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
req.Header.Set("Authorization", "Bearer "+c.Token)
|
|
1299
|
+
req.Header.Set("Content-Type", "application/json")
|
|
1300
|
+
req.Header.Set("Accept", "application/json")
|
|
1301
|
+
|
|
1302
|
+
return c.HTTPClient.Do(req)
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
type User struct {
|
|
1306
|
+
ID string `json:"id"`
|
|
1307
|
+
Email string `json:"email"`
|
|
1308
|
+
Name string `json:"name"`
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
func (c *Client) ListUsers(page int) ([]User, error) {
|
|
1312
|
+
resp, err := c.doRequest("GET", fmt.Sprintf("/users?page=%d", page), nil)
|
|
1313
|
+
if err != nil {
|
|
1314
|
+
return nil, err
|
|
1315
|
+
}
|
|
1316
|
+
defer resp.Body.Close()
|
|
1317
|
+
|
|
1318
|
+
var result struct {
|
|
1319
|
+
Data []User `json:"data"`
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
1323
|
+
return nil, err
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
return result.Data, nil
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
func (c *Client) CreateUser(user User) (*User, error) {
|
|
1330
|
+
body, _ := json.Marshal(user)
|
|
1331
|
+
resp, err := c.doRequest("POST", "/users", bytes.NewReader(body))
|
|
1332
|
+
if err != nil {
|
|
1333
|
+
return nil, err
|
|
1334
|
+
}
|
|
1335
|
+
defer resp.Body.Close()
|
|
1336
|
+
|
|
1337
|
+
var result struct {
|
|
1338
|
+
Data User `json:"data"`
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
1342
|
+
return nil, err
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
return &result.Data, nil
|
|
1346
|
+
}
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
PHASE 8: GUIDES AND WALKTHROUGNS
|
|
1351
|
+
|
|
1352
|
+
provide narrative documentation beyond reference.
|
|
1353
|
+
|
|
1354
|
+
|
|
1355
|
+
quick start guide
|
|
1356
|
+
|
|
1357
|
+
## Quick Start
|
|
1358
|
+
|
|
1359
|
+
Get started with the API in 5 minutes.
|
|
1360
|
+
|
|
1361
|
+
### 1. Get Your Credentials
|
|
1362
|
+
|
|
1363
|
+
Sign up at [https://example.com/signup](https://example.com/signup) and get your API key from the dashboard.
|
|
1364
|
+
|
|
1365
|
+
### 2. Make Your First Request
|
|
1366
|
+
|
|
1367
|
+
```bash
|
|
1368
|
+
curl "https://api.example.com/v1/users" \
|
|
1369
|
+
-H "X-API-Key: YOUR_API_KEY"
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
Response:
|
|
1373
|
+
```json
|
|
1374
|
+
{
|
|
1375
|
+
"data": [],
|
|
1376
|
+
"pagination": {
|
|
1377
|
+
"page": 1,
|
|
1378
|
+
"limit": 20,
|
|
1379
|
+
"total": 0,
|
|
1380
|
+
"pages": 0
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1385
|
+
### 3. Create Your First Resource
|
|
1386
|
+
|
|
1387
|
+
```bash
|
|
1388
|
+
curl -X POST "https://api.example.com/v1/users" \
|
|
1389
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
1390
|
+
-H "Content-Type: application/json" \
|
|
1391
|
+
-d '{
|
|
1392
|
+
"email": "user@example.com",
|
|
1393
|
+
"name": "Hello World"
|
|
1394
|
+
}'
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
### 4. Explore
|
|
1398
|
+
|
|
1399
|
+
Browse the [full API reference](#reference) or check out more [guides](#guides).
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
common workflows
|
|
1403
|
+
|
|
1404
|
+
### Pagination
|
|
1405
|
+
|
|
1406
|
+
List endpoints support pagination:
|
|
1407
|
+
|
|
1408
|
+
```python
|
|
1409
|
+
import requests
|
|
1410
|
+
|
|
1411
|
+
def get_all_users():
|
|
1412
|
+
page = 1
|
|
1413
|
+
all_users = []
|
|
1414
|
+
|
|
1415
|
+
while True:
|
|
1416
|
+
response = requests.get(
|
|
1417
|
+
"https://api.example.com/v1/users",
|
|
1418
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
1419
|
+
params={"page": page, "limit": 100}
|
|
1420
|
+
).json()
|
|
1421
|
+
|
|
1422
|
+
all_users.extend(response["data"])
|
|
1423
|
+
|
|
1424
|
+
if not response["pagination"]["has_next"]:
|
|
1425
|
+
break
|
|
1426
|
+
|
|
1427
|
+
page += 1
|
|
1428
|
+
|
|
1429
|
+
return all_users
|
|
1430
|
+
```
|
|
1431
|
+
|
|
1432
|
+
### Filtering and Sorting
|
|
1433
|
+
|
|
1434
|
+
```bash
|
|
1435
|
+
# Filter active users, sorted by name
|
|
1436
|
+
curl "https://api.example.com/v1/users?filter[status]=active&sort=name_asc" \
|
|
1437
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
1438
|
+
|
|
1439
|
+
# Complex filtering
|
|
1440
|
+
curl "https://api.example.com/v1/users?filter[age][gte]=18&filter[status]=active&sort=created_desc" \
|
|
1441
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
1442
|
+
```
|
|
1443
|
+
|
|
1444
|
+
### Error Handling
|
|
1445
|
+
|
|
1446
|
+
```python
|
|
1447
|
+
import requests
|
|
1448
|
+
from requests.exceptions import HTTPError
|
|
1449
|
+
|
|
1450
|
+
def api_call(method, endpoint, **kwargs):
|
|
1451
|
+
try:
|
|
1452
|
+
response = requests.request(
|
|
1453
|
+
method,
|
|
1454
|
+
f"https://api.example.com/v1{endpoint}",
|
|
1455
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
1456
|
+
**kwargs
|
|
1457
|
+
)
|
|
1458
|
+
response.raise_for_status()
|
|
1459
|
+
return response.json()
|
|
1460
|
+
|
|
1461
|
+
except HTTPError as e:
|
|
1462
|
+
error_data = e.response.json()
|
|
1463
|
+
print(f"Error: {error_data['error']['code']}")
|
|
1464
|
+
print(f"Message: {error_data['error']['message']}")
|
|
1465
|
+
|
|
1466
|
+
# Handle specific error codes
|
|
1467
|
+
if error_data['error']['code'] == 'rate_limit_exceeded':
|
|
1468
|
+
# Implement backoff retry
|
|
1469
|
+
pass
|
|
1470
|
+
elif error_data['error']['code'] == 'unauthorized':
|
|
1471
|
+
# Refresh token
|
|
1472
|
+
pass
|
|
1473
|
+
|
|
1474
|
+
raise
|
|
1475
|
+
```
|
|
1476
|
+
|
|
1477
|
+
### Webhooks
|
|
1478
|
+
|
|
1479
|
+
Set up webhooks to receive notifications:
|
|
1480
|
+
|
|
1481
|
+
```python
|
|
1482
|
+
from flask import Flask, request, jsonify
|
|
1483
|
+
|
|
1484
|
+
app = Flask(__name__)
|
|
1485
|
+
|
|
1486
|
+
@app.route('/webhook', methods=['POST'])
|
|
1487
|
+
def handle_webhook():
|
|
1488
|
+
# Verify signature
|
|
1489
|
+
signature = request.headers.get('X-Webhook-Signature')
|
|
1490
|
+
if not verify_signature(signature, request.data):
|
|
1491
|
+
return jsonify({"error": "invalid signature"}), 401
|
|
1492
|
+
|
|
1493
|
+
# Process event
|
|
1494
|
+
event = request.json
|
|
1495
|
+
event_type = event['type']
|
|
1496
|
+
|
|
1497
|
+
if event_type == 'user.created':
|
|
1498
|
+
handle_user_created(event['data'])
|
|
1499
|
+
elif event_type == 'user.deleted':
|
|
1500
|
+
handle_user_deleted(event['data'])
|
|
1501
|
+
|
|
1502
|
+
return jsonify({"status": "received"}), 200
|
|
1503
|
+
```
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
PHASE 9: VERSIONING DOCUMENTATION
|
|
1507
|
+
|
|
1508
|
+
clearly document API versioning strategy.
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
versioning overview
|
|
1512
|
+
|
|
1513
|
+
## API Versioning
|
|
1514
|
+
|
|
1515
|
+
The API uses URL-based versioning. The current version is `v1`.
|
|
1516
|
+
|
|
1517
|
+
### Version Format
|
|
1518
|
+
|
|
1519
|
+
```
|
|
1520
|
+
https://api.example.com/v{version}/{resource}
|
|
1521
|
+
```
|
|
1522
|
+
|
|
1523
|
+
Example:
|
|
1524
|
+
```
|
|
1525
|
+
https://api.example.com/v1/users
|
|
1526
|
+
https://api.example.com/v2/users
|
|
1527
|
+
```
|
|
1528
|
+
|
|
1529
|
+
### Supported Versions
|
|
1530
|
+
|
|
1531
|
+
| Version | Status | Release Date | Deprecation Date | Sunset Date |
|
|
1532
|
+
|---------|--------|--------------|------------------|-------------|
|
|
1533
|
+
| v2 | Current | 2024-06-01 | - | - |
|
|
1534
|
+
| v1 | Deprecated | 2023-01-01 | 2024-06-01 | 2025-01-01 |
|
|
1535
|
+
|
|
1536
|
+
### Deprecation Policy
|
|
1537
|
+
|
|
1538
|
+
- API versions are supported for at least 12 months after deprecation
|
|
1539
|
+
- Deprecated versions return a `Deprecation` header
|
|
1540
|
+
- Sunset dates are announced at least 6 months in advance
|
|
1541
|
+
|
|
1542
|
+
Deprecation header:
|
|
1543
|
+
```
|
|
1544
|
+
Deprecation: true
|
|
1545
|
+
Sunset: Sat, 01 Jan 2025 00:00:00 GMT
|
|
1546
|
+
Link: <https://docs.example.com/api/v2>; rel="successor-version"
|
|
1547
|
+
```
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
version changes documentation
|
|
1551
|
+
|
|
1552
|
+
### Migrating from v1 to v2
|
|
1553
|
+
|
|
1554
|
+
Breaking changes in v2:
|
|
1555
|
+
|
|
1556
|
+
[1] Response format change
|
|
1557
|
+
v1: `{ "users": [...] }`
|
|
1558
|
+
v2: `{ "data": [...] }`
|
|
1559
|
+
|
|
1560
|
+
Migration: Update response parsing:
|
|
1561
|
+
```python
|
|
1562
|
+
# v1
|
|
1563
|
+
users = response.json()["users"]
|
|
1564
|
+
|
|
1565
|
+
# v2
|
|
1566
|
+
users = response.json()["data"]
|
|
1567
|
+
```
|
|
1568
|
+
|
|
1569
|
+
[2] Authentication header changed
|
|
1570
|
+
v1: `X-Auth-Token: <token>`
|
|
1571
|
+
v2: `Authorization: Bearer <token>`
|
|
1572
|
+
|
|
1573
|
+
Migration: Update authentication headers
|
|
1574
|
+
|
|
1575
|
+
[3] User ID format changed
|
|
1576
|
+
v1: Integer IDs (`123`)
|
|
1577
|
+
v2: String IDs (`usr_abc123xyz`)
|
|
1578
|
+
|
|
1579
|
+
Migration: Update ID handling code
|
|
1580
|
+
|
|
1581
|
+
[4] Pagination parameter renamed
|
|
1582
|
+
v1: `per_page`
|
|
1583
|
+
v2: `limit`
|
|
1584
|
+
|
|
1585
|
+
Migration: Update parameter names
|
|
1586
|
+
|
|
1587
|
+
Non-breaking additions in v2:
|
|
1588
|
+
|
|
1589
|
+
- New filtering options
|
|
1590
|
+
- Webhook support
|
|
1591
|
+
- Batch operations
|
|
1592
|
+
- Rate limit headers
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
PHASE 10: TESTING AND EXAMPLES
|
|
1596
|
+
|
|
1597
|
+
provide testable examples and sandbox.
|
|
1598
|
+
|
|
1599
|
+
|
|
1600
|
+
sandbox environment
|
|
1601
|
+
|
|
1602
|
+
## API Sandbox
|
|
1603
|
+
|
|
1604
|
+
Test the API without affecting real data.
|
|
1605
|
+
|
|
1606
|
+
### Sandbox URL
|
|
1607
|
+
|
|
1608
|
+
```
|
|
1609
|
+
https://sandbox-api.example.com/v1
|
|
1610
|
+
```
|
|
1611
|
+
|
|
1612
|
+
The sandbox provides:
|
|
1613
|
+
- Full API functionality
|
|
1614
|
+
- Isolated test data
|
|
1615
|
+
- No rate limiting
|
|
1616
|
+
- Pre-configured test accounts
|
|
1617
|
+
|
|
1618
|
+
### Sandbox Credentials
|
|
1619
|
+
|
|
1620
|
+
```bash
|
|
1621
|
+
# Sandbox test account
|
|
1622
|
+
curl "https://sandbox-api.example.com/v1/auth/token" \
|
|
1623
|
+
-H "Content-Type: application/json" \
|
|
1624
|
+
-d '{
|
|
1625
|
+
"client_id": "test_client_id",
|
|
1626
|
+
"client_secret": "test_client_secret",
|
|
1627
|
+
"grant_type": "client_credentials"
|
|
1628
|
+
}'
|
|
1629
|
+
```
|
|
1630
|
+
|
|
1631
|
+
Response:
|
|
1632
|
+
```json
|
|
1633
|
+
{
|
|
1634
|
+
"access_token": "test_token_abc123",
|
|
1635
|
+
"token_type": "Bearer",
|
|
1636
|
+
"expires_in": 3600
|
|
1637
|
+
}
|
|
1638
|
+
```
|
|
1639
|
+
|
|
1640
|
+
|
|
1641
|
+
example data sets
|
|
1642
|
+
|
|
1643
|
+
provide realistic example data:
|
|
1644
|
+
|
|
1645
|
+
### Example User Object
|
|
1646
|
+
|
|
1647
|
+
```json
|
|
1648
|
+
{
|
|
1649
|
+
"id": "usr_abc123xyz",
|
|
1650
|
+
"email": "alice.johnson@example.com",
|
|
1651
|
+
"name": "Alice Johnson",
|
|
1652
|
+
"avatar_url": "https://cdn.example.com/avatars/usr_abc123xyz.jpg",
|
|
1653
|
+
"status": "active",
|
|
1654
|
+
"role": "user",
|
|
1655
|
+
"timezone": "America/New_York",
|
|
1656
|
+
"locale": "en",
|
|
1657
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
1658
|
+
"updated_at": "2024-01-20T14:22:00Z",
|
|
1659
|
+
"last_login_at": "2024-01-25T09:15:00Z",
|
|
1660
|
+
"profile": {
|
|
1661
|
+
"bio": "Software developer and coffee enthusiast",
|
|
1662
|
+
"location": "San Francisco, CA",
|
|
1663
|
+
"website": "https://alice.example.com",
|
|
1664
|
+
"twitter": "@alicejohnson"
|
|
1665
|
+
},
|
|
1666
|
+
"stats": {
|
|
1667
|
+
"posts_count": 42,
|
|
1668
|
+
"followers_count": 156,
|
|
1669
|
+
"following_count": 89
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
```
|
|
1673
|
+
|
|
1674
|
+
### Example Error Response
|
|
1675
|
+
|
|
1676
|
+
```json
|
|
1677
|
+
{
|
|
1678
|
+
"error": {
|
|
1679
|
+
"code": "validation_error",
|
|
1680
|
+
"message": "Validation failed for one or more fields",
|
|
1681
|
+
"details": [
|
|
1682
|
+
{
|
|
1683
|
+
"field": "email",
|
|
1684
|
+
"message": "Email is already registered",
|
|
1685
|
+
"code": "duplicate_email"
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
"field": "password",
|
|
1689
|
+
"message": "Password must be at least 8 characters",
|
|
1690
|
+
"code": "password_too_short"
|
|
1691
|
+
}
|
|
1692
|
+
],
|
|
1693
|
+
"request_id": "req_xyz789abc",
|
|
1694
|
+
"timestamp": "2024-01-25T10:30:00Z"
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
```
|
|
1698
|
+
|
|
1699
|
+
|
|
1700
|
+
PHASE 11: RATE LIMITING DOCUMENTATION
|
|
1701
|
+
|
|
1702
|
+
explain rate limits clearly.
|
|
1703
|
+
|
|
1704
|
+
|
|
1705
|
+
rate limits overview
|
|
1706
|
+
|
|
1707
|
+
## Rate Limiting
|
|
1708
|
+
|
|
1709
|
+
To ensure fair usage, the API enforces rate limits.
|
|
1710
|
+
|
|
1711
|
+
### Rate Limit Tiers
|
|
1712
|
+
|
|
1713
|
+
| Plan | Requests per hour | Requests per day | Burst allowance |
|
|
1714
|
+
|------|-------------------|------------------|-----------------|
|
|
1715
|
+
| Free | 1,000 | 10,000 | 50 |
|
|
1716
|
+
| Basic | 10,000 | 100,000 | 200 |
|
|
1717
|
+
| Pro | 100,000 | 1,000,000 | 1,000 |
|
|
1718
|
+
| Enterprise | Unlimited | Unlimited | 10,000 |
|
|
1719
|
+
|
|
1720
|
+
### Rate Limit Headers
|
|
1721
|
+
|
|
1722
|
+
Every response includes rate limit information:
|
|
1723
|
+
|
|
1724
|
+
```
|
|
1725
|
+
X-RateLimit-Limit: 1000
|
|
1726
|
+
X-RateLimit-Remaining: 945
|
|
1727
|
+
X-RateLimit-Reset: 1706169600
|
|
1728
|
+
X-RateLimit-Reset-After: 342
|
|
1729
|
+
```
|
|
1730
|
+
|
|
1731
|
+
Header descriptions:
|
|
1732
|
+
- `X-RateLimit-Limit`: Maximum requests per window
|
|
1733
|
+
- `X-RateLimit-Remaining`: Requests remaining in window
|
|
1734
|
+
- `X-RateLimit-Reset`: Unix timestamp when limit resets
|
|
1735
|
+
- `X-RateLimit-Reset-After`: Seconds until reset
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
handling rate limits
|
|
1739
|
+
|
|
1740
|
+
### Implementing Retry Logic
|
|
1741
|
+
|
|
1742
|
+
```python
|
|
1743
|
+
import time
|
|
1744
|
+
import requests
|
|
1745
|
+
|
|
1746
|
+
def api_request_with_retry(url, max_retries=5):
|
|
1747
|
+
retries = 0
|
|
1748
|
+
|
|
1749
|
+
while retries < max_retries:
|
|
1750
|
+
response = requests.get(url, headers=headers)
|
|
1751
|
+
|
|
1752
|
+
# Check rate limit status
|
|
1753
|
+
remaining = int(response.headers.get('X-RateLimit-Remaining', 1))
|
|
1754
|
+
if remaining < 10:
|
|
1755
|
+
# Proactively slow down
|
|
1756
|
+
time.sleep(1)
|
|
1757
|
+
|
|
1758
|
+
# Handle rate limit
|
|
1759
|
+
if response.status_code == 429:
|
|
1760
|
+
reset_after = int(response.headers.get('Retry-After', 60))
|
|
1761
|
+
print(f"Rate limited. Waiting {reset_after} seconds...")
|
|
1762
|
+
time.sleep(reset_after)
|
|
1763
|
+
retries += 1
|
|
1764
|
+
continue
|
|
1765
|
+
|
|
1766
|
+
return response
|
|
1767
|
+
|
|
1768
|
+
raise Exception("Max retries exceeded due to rate limiting")
|
|
1769
|
+
```
|
|
1770
|
+
|
|
1771
|
+
### Exponential Backoff
|
|
1772
|
+
|
|
1773
|
+
```javascript
|
|
1774
|
+
async function fetchWithBackoff(url, maxRetries = 5) {
|
|
1775
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1776
|
+
const response = await fetch(url, { headers });
|
|
1777
|
+
|
|
1778
|
+
if (response.status !== 429) {
|
|
1779
|
+
return response;
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
// Calculate backoff with jitter
|
|
1783
|
+
const baseDelay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s, 16s
|
|
1784
|
+
const jitter = Math.random() * 1000;
|
|
1785
|
+
const delay = baseDelay + jitter;
|
|
1786
|
+
|
|
1787
|
+
console.log(`Rate limited. Retrying after ${delay}ms...`);
|
|
1788
|
+
await new Promise(r => setTimeout(r, delay));
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
throw new Error('Max retries exceeded');
|
|
1792
|
+
}
|
|
1793
|
+
```
|
|
1794
|
+
|
|
1795
|
+
|
|
1796
|
+
PHASE 12: WEBHOOKS DOCUMENTATION
|
|
1797
|
+
|
|
1798
|
+
document webhook system thoroughly.
|
|
1799
|
+
|
|
1800
|
+
|
|
1801
|
+
webhook overview
|
|
1802
|
+
|
|
1803
|
+
## Webhooks
|
|
1804
|
+
|
|
1805
|
+
Webhooks allow your application to receive real-time notifications when events occur.
|
|
1806
|
+
|
|
1807
|
+
### Setting Up Webhooks
|
|
1808
|
+
|
|
1809
|
+
1. Provide a publicly accessible HTTPS endpoint
|
|
1810
|
+
2. Register the webhook URL via the API
|
|
1811
|
+
3. Verify your ownership of the URL
|
|
1812
|
+
4. Start receiving events
|
|
1813
|
+
|
|
1814
|
+
### Creating a Webhook
|
|
1815
|
+
|
|
1816
|
+
```bash
|
|
1817
|
+
curl -X POST "https://api.example.com/v1/webhooks" \
|
|
1818
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
1819
|
+
-H "Content-Type: application/json" \
|
|
1820
|
+
-d '{
|
|
1821
|
+
"url": "https://your-app.com/webhooks",
|
|
1822
|
+
"events": ["user.created", "user.updated", "user.deleted"],
|
|
1823
|
+
"secret": "your_webhook_secret"
|
|
1824
|
+
}'
|
|
1825
|
+
```
|
|
1826
|
+
|
|
1827
|
+
Response:
|
|
1828
|
+
```json
|
|
1829
|
+
{
|
|
1830
|
+
"data": {
|
|
1831
|
+
"id": "whk_abc123",
|
|
1832
|
+
"url": "https://your-app.com/webhooks",
|
|
1833
|
+
"events": ["user.created", "user.updated", "user.deleted"],
|
|
1834
|
+
"status": "active",
|
|
1835
|
+
"created_at": "2024-01-25T10:30:00Z"
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
```
|
|
1839
|
+
|
|
1840
|
+
|
|
1841
|
+
webhook events
|
|
1842
|
+
|
|
1843
|
+
### Available Events
|
|
1844
|
+
|
|
1845
|
+
| Event | Description | Payload |
|
|
1846
|
+
|-------|-------------|---------|
|
|
1847
|
+
| `user.created` | New user registered | User object |
|
|
1848
|
+
| `user.updated` | User profile changed | User object + changes |
|
|
1849
|
+
| `user.deleted` | User account deleted | User ID |
|
|
1850
|
+
| `payment.succeeded` | Payment completed | Payment object |
|
|
1851
|
+
| `payment.failed` | Payment failed | Payment + error |
|
|
1852
|
+
| `subscription.started` | Subscription created | Subscription object |
|
|
1853
|
+
| `subscription.ended` | Subscription cancelled | Subscription object |
|
|
1854
|
+
|
|
1855
|
+
### Event Payload Structure
|
|
1856
|
+
|
|
1857
|
+
```json
|
|
1858
|
+
{
|
|
1859
|
+
"id": "evt_abc123xyz",
|
|
1860
|
+
"type": "user.created",
|
|
1861
|
+
"data": {
|
|
1862
|
+
"id": "usr_def456",
|
|
1863
|
+
"email": "newuser@example.com",
|
|
1864
|
+
"name": "New User",
|
|
1865
|
+
"created_at": "2024-01-25T10:30:00Z"
|
|
1866
|
+
},
|
|
1867
|
+
"timestamp": "2024-01-25T10:30:00Z",
|
|
1868
|
+
"delivered_attempts": 1
|
|
1869
|
+
}
|
|
1870
|
+
```
|
|
1871
|
+
|
|
1872
|
+
|
|
1873
|
+
webhook verification
|
|
1874
|
+
|
|
1875
|
+
### Verifying Webhook Signatures
|
|
1876
|
+
|
|
1877
|
+
Each webhook includes a signature header:
|
|
1878
|
+
|
|
1879
|
+
```
|
|
1880
|
+
X-Webhook-Signature: t=1643104800,v1=abc123...
|
|
1881
|
+
```
|
|
1882
|
+
|
|
1883
|
+
Verification logic:
|
|
1884
|
+
|
|
1885
|
+
```python
|
|
1886
|
+
import hmac
|
|
1887
|
+
import hashlib
|
|
1888
|
+
from flask import Flask, request, jsonify
|
|
1889
|
+
|
|
1890
|
+
app = Flask(__name__)
|
|
1891
|
+
WEBHOOK_SECRET = "your_webhook_secret"
|
|
1892
|
+
|
|
1893
|
+
def verify_signature(payload, signature_header):
|
|
1894
|
+
# Split signature
|
|
1895
|
+
parts = signature_header.split(',')
|
|
1896
|
+
signature_dict = {}
|
|
1897
|
+
|
|
1898
|
+
for part in parts:
|
|
1899
|
+
key, value = part.split('=')
|
|
1900
|
+
signature_dict[key] = value
|
|
1901
|
+
|
|
1902
|
+
# Create expected signature
|
|
1903
|
+
expected_payload = f"{signature_dict['t']}.{payload}"
|
|
1904
|
+
expected_signature = hmac.new(
|
|
1905
|
+
WEBHOOK_SECRET.encode(),
|
|
1906
|
+
expected_payload.encode(),
|
|
1907
|
+
hashlib.sha256
|
|
1908
|
+
).hexdigest()
|
|
1909
|
+
|
|
1910
|
+
# Compare signatures
|
|
1911
|
+
received_signature = signature_dict.get('v1', '')
|
|
1912
|
+
return hmac.compare_digest(expected_signature, received_signature)
|
|
1913
|
+
|
|
1914
|
+
@app.route('/webhooks', methods=['POST'])
|
|
1915
|
+
def handle_webhook():
|
|
1916
|
+
signature = request.headers.get('X-Webhook-Signature')
|
|
1917
|
+
payload = request.get_data(as_text=True)
|
|
1918
|
+
|
|
1919
|
+
if not verify_signature(payload, signature):
|
|
1920
|
+
return jsonify({"error": "Invalid signature"}), 401
|
|
1921
|
+
|
|
1922
|
+
# Process webhook
|
|
1923
|
+
event = request.json
|
|
1924
|
+
# ... handle event ...
|
|
1925
|
+
|
|
1926
|
+
return jsonify({"status": "ok"}), 200
|
|
1927
|
+
```
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
webhook best practices
|
|
1931
|
+
|
|
1932
|
+
[ ] Always verify signatures
|
|
1933
|
+
[ ] Return 200 OK quickly, process asynchronously
|
|
1934
|
+
[ ] Handle duplicate events (idempotency)
|
|
1935
|
+
[ ] Implement retry logic for failures
|
|
1936
|
+
[ ] Log all webhook events
|
|
1937
|
+
[ ] Use HTTPS only
|
|
1938
|
+
[ ] Don't expose sensitive data in webhook URLs
|
|
1939
|
+
|
|
1940
|
+
example webhook handler:
|
|
1941
|
+
|
|
1942
|
+
```python
|
|
1943
|
+
import hashlib
|
|
1944
|
+
from typing import Dict
|
|
1945
|
+
from datetime import datetime
|
|
1946
|
+
|
|
1947
|
+
class WebhookHandler:
|
|
1948
|
+
def __init__(self, secret: str):
|
|
1949
|
+
self.secret = secret
|
|
1950
|
+
self.processed_events = set() # In production, use Redis or database
|
|
1951
|
+
|
|
1952
|
+
def verify(self, payload: str, signature: str) -> bool:
|
|
1953
|
+
"""Verify webhook signature."""
|
|
1954
|
+
expected = hmac.new(
|
|
1955
|
+
self.secret.encode(),
|
|
1956
|
+
payload.encode(),
|
|
1957
|
+
hashlib.sha256
|
|
1958
|
+
).hexdigest()
|
|
1959
|
+
return hmac.compare_digest(expected, signature)
|
|
1960
|
+
|
|
1961
|
+
def is_duplicate(self, event_id: str) -> bool:
|
|
1962
|
+
"""Check if event was already processed."""
|
|
1963
|
+
return event_id in self.processed_events
|
|
1964
|
+
|
|
1965
|
+
def mark_processed(self, event_id: str):
|
|
1966
|
+
"""Mark event as processed."""
|
|
1967
|
+
self.processed_events.add(event_id)
|
|
1968
|
+
|
|
1969
|
+
def handle(self, event: Dict) -> None:
|
|
1970
|
+
"""Handle webhook event."""
|
|
1971
|
+
event_id = event['id']
|
|
1972
|
+
|
|
1973
|
+
if self.is_duplicate(event_id):
|
|
1974
|
+
print(f"Duplicate event {event_id}, skipping")
|
|
1975
|
+
return
|
|
1976
|
+
|
|
1977
|
+
# Route to appropriate handler
|
|
1978
|
+
handlers = {
|
|
1979
|
+
'user.created': self.handle_user_created,
|
|
1980
|
+
'user.updated': self.handle_user_updated,
|
|
1981
|
+
'user.deleted': self.handle_user_deleted,
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
handler = handlers.get(event['type'])
|
|
1985
|
+
if handler:
|
|
1986
|
+
handler(event['data'])
|
|
1987
|
+
self.mark_processed(event_id)
|
|
1988
|
+
|
|
1989
|
+
def handle_user_created(self, data):
|
|
1990
|
+
"""Handle user created event."""
|
|
1991
|
+
print(f"New user: {data['email']}")
|
|
1992
|
+
|
|
1993
|
+
# ... other handlers ...
|
|
1994
|
+
```
|
|
1995
|
+
|
|
1996
|
+
|
|
1997
|
+
PHASE 13: CHANGELOG DOCUMENTATION
|
|
1998
|
+
|
|
1999
|
+
maintain a history of API changes.
|
|
2000
|
+
|
|
2001
|
+
|
|
2002
|
+
changelog format
|
|
2003
|
+
|
|
2004
|
+
## Changelog
|
|
2005
|
+
|
|
2006
|
+
All notable changes to the API are documented in this file.
|
|
2007
|
+
|
|
2008
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
2009
|
+
|
|
2010
|
+
### [Unreleased]
|
|
2011
|
+
|
|
2012
|
+
### [2.1.0] - 2024-02-15
|
|
2013
|
+
|
|
2014
|
+
#### Added
|
|
2015
|
+
- New `include` parameter for related resources
|
|
2016
|
+
- Webhook support for user events
|
|
2017
|
+
- Filter by multiple statuses
|
|
2018
|
+
|
|
2019
|
+
#### Changed
|
|
2020
|
+
- Increased rate limits for Pro plan
|
|
2021
|
+
- Improved error messages for validation failures
|
|
2022
|
+
|
|
2023
|
+
#### Fixed
|
|
2024
|
+
- Fixed pagination issue with filtered results
|
|
2025
|
+
- Fixed timezone handling in date fields
|
|
2026
|
+
|
|
2027
|
+
#### Deprecated
|
|
2028
|
+
- `per_page` parameter (use `limit` instead)
|
|
2029
|
+
|
|
2030
|
+
#### Removed
|
|
2031
|
+
- XML response format
|
|
2032
|
+
- v0 endpoints (grace period ended)
|
|
2033
|
+
|
|
2034
|
+
#### Security
|
|
2035
|
+
- Added signature verification for webhooks
|
|
2036
|
+
|
|
2037
|
+
### [2.0.0] - 2024-01-01
|
|
2038
|
+
|
|
2039
|
+
#### Breaking Changes
|
|
2040
|
+
- Response format changed to `{ data, meta }` structure
|
|
2041
|
+
- Authentication now uses `Authorization: Bearer` header
|
|
2042
|
+
- User IDs are now strings instead of integers
|
|
2043
|
+
|
|
2044
|
+
See [migration guide](/docs/migration-v2) for details.
|
|
2045
|
+
|
|
2046
|
+
#### Added
|
|
2047
|
+
- Batch operations endpoint
|
|
2048
|
+
- Server-sent events for real-time updates
|
|
2049
|
+
- Rate limit headers on all responses
|
|
2050
|
+
|
|
2051
|
+
### [1.5.0] - 2023-11-15
|
|
2052
|
+
|
|
2053
|
+
#### Added
|
|
2054
|
+
- Search endpoint
|
|
2055
|
+
- Webhook management endpoints
|
|
2056
|
+
|
|
2057
|
+
#### Changed
|
|
2058
|
+
- Default page size increased from 10 to 20
|
|
2059
|
+
|
|
2060
|
+
#### Deprecated
|
|
2061
|
+
- Legacy authentication method
|
|
2062
|
+
|
|
2063
|
+
|
|
2064
|
+
migration guides
|
|
2065
|
+
|
|
2066
|
+
for major versions, provide detailed migration guides:
|
|
2067
|
+
|
|
2068
|
+
### Migrating to v2.0
|
|
2069
|
+
|
|
2070
|
+
This guide helps you migrate from v1 to v2.
|
|
2071
|
+
|
|
2072
|
+
#### Overview of Changes
|
|
2073
|
+
|
|
2074
|
+
[1] Authentication
|
|
2075
|
+
v1: `X-Auth-Token` header
|
|
2076
|
+
v2: `Authorization: Bearer` header
|
|
2077
|
+
|
|
2078
|
+
[2] Response Structure
|
|
2079
|
+
v1: `{ resource_name: [...] }`
|
|
2080
|
+
v2: `{ data: [...], meta: {...} }`
|
|
2081
|
+
|
|
2082
|
+
[3] ID Format
|
|
2083
|
+
v1: Integer IDs
|
|
2084
|
+
v2: String IDs with prefixes
|
|
2085
|
+
|
|
2086
|
+
[4] Pagination
|
|
2087
|
+
v1: `per_page` parameter
|
|
2088
|
+
v2: `limit` parameter
|
|
2089
|
+
|
|
2090
|
+
#### Code Changes
|
|
2091
|
+
|
|
2092
|
+
Before (v1):
|
|
2093
|
+
```python
|
|
2094
|
+
response = requests.get('https://api.example.com/v1/users', headers={
|
|
2095
|
+
'X-Auth-Token': token
|
|
2096
|
+
})
|
|
2097
|
+
users = response.json()['users']
|
|
2098
|
+
```
|
|
2099
|
+
|
|
2100
|
+
After (v2):
|
|
2101
|
+
```python
|
|
2102
|
+
response = requests.get('https://api.example.com/v2/users', headers={
|
|
2103
|
+
'Authorization': f'Bearer {token}'
|
|
2104
|
+
})
|
|
2105
|
+
users = response.json()['data']
|
|
2106
|
+
```
|
|
2107
|
+
|
|
2108
|
+
#### Testing Your Migration
|
|
2109
|
+
|
|
2110
|
+
Use the v2 sandbox environment:
|
|
2111
|
+
```
|
|
2112
|
+
https://sandbox-api.example.com/v2
|
|
2113
|
+
```
|
|
2114
|
+
|
|
2115
|
+
Follow the [testing checklist](#testing-checklist).
|
|
2116
|
+
|
|
2117
|
+
|
|
2118
|
+
PHASE 14: API DOCUMENTATION CHECKLIST
|
|
2119
|
+
|
|
2120
|
+
|
|
2121
|
+
content completeness
|
|
2122
|
+
|
|
2123
|
+
[ ] overview section
|
|
2124
|
+
[ ] what the API does
|
|
2125
|
+
[ ] who it's for
|
|
2126
|
+
[ ] key features
|
|
2127
|
+
|
|
2128
|
+
[ ] authentication section
|
|
2129
|
+
[ ] all auth methods documented
|
|
2130
|
+
[ ] how to get credentials
|
|
2131
|
+
[ ] token refresh process
|
|
2132
|
+
[ ] scopes and permissions
|
|
2133
|
+
|
|
2134
|
+
[ ] all endpoints documented
|
|
2135
|
+
[ ] HTTP method and path
|
|
2136
|
+
[ ] description
|
|
2137
|
+
[ ] all parameters documented
|
|
2138
|
+
[ ] all headers documented
|
|
2139
|
+
[ ] request body schema
|
|
2140
|
+
[ ] all response codes
|
|
2141
|
+
[ ] response schemas
|
|
2142
|
+
[ ] examples for each
|
|
2143
|
+
|
|
2144
|
+
[ ] common patterns documented
|
|
2145
|
+
[ ] pagination
|
|
2146
|
+
[ ] filtering
|
|
2147
|
+
[ ] sorting
|
|
2148
|
+
[ ] error handling
|
|
2149
|
+
[ ] rate limiting
|
|
2150
|
+
|
|
2151
|
+
[ ] code examples
|
|
2152
|
+
[ ] curl
|
|
2153
|
+
[ ] javascript
|
|
2154
|
+
[ ] python
|
|
2155
|
+
[ ] at least one backend language
|
|
2156
|
+
|
|
2157
|
+
[ ] guides
|
|
2158
|
+
[ ] quick start
|
|
2159
|
+
[ ] common workflows
|
|
2160
|
+
[ ] integration examples
|
|
2161
|
+
[ ] troubleshooting
|
|
2162
|
+
|
|
2163
|
+
|
|
2164
|
+
quality checks
|
|
2165
|
+
|
|
2166
|
+
[ ] all examples are tested
|
|
2167
|
+
[ ] curl commands run successfully
|
|
2168
|
+
[ ] code examples execute
|
|
2169
|
+
[ ] example responses are accurate
|
|
2170
|
+
|
|
2171
|
+
[ ] OpenAPI spec is valid
|
|
2172
|
+
<terminal>npm install -g @apidevtools/swagger-cli</terminal>
|
|
2173
|
+
<terminal>swagger-cli validate openapi.yaml</terminal>
|
|
2174
|
+
|
|
2175
|
+
[ ] links work
|
|
2176
|
+
[ ] internal links
|
|
2177
|
+
[ ] external links
|
|
2178
|
+
[ ] code links
|
|
2179
|
+
|
|
2180
|
+
[ ] consistent terminology
|
|
2181
|
+
[ ] always use same terms for concepts
|
|
2182
|
+
[ ] avoid synonyms
|
|
2183
|
+
[ ] define jargon
|
|
2184
|
+
|
|
2185
|
+
[ ] clear and concise
|
|
2186
|
+
[ ] no unnecessary verbosity
|
|
2187
|
+
[ ] no ambiguity
|
|
2188
|
+
[ ] active voice
|
|
2189
|
+
|
|
2190
|
+
[ ] accessible
|
|
2191
|
+
[ ] readable without styling
|
|
2192
|
+
[ ] alt text for images
|
|
2193
|
+
[ ] color contrast
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
PHASE 15: API DOCUMENTATION RULES (STRICT MODE)
|
|
2197
|
+
|
|
2198
|
+
|
|
2199
|
+
while this skill is active, these rules are MANDATORY:
|
|
2200
|
+
|
|
2201
|
+
[1] DOCUMENT EVERY ENDPOINT
|
|
2202
|
+
no endpoint should be undocumented
|
|
2203
|
+
if an endpoint exists publicly, it must be documented
|
|
2204
|
+
|
|
2205
|
+
[2] NEVER SKIP ERROR RESPONSES
|
|
2206
|
+
document all possible error codes for each endpoint
|
|
2207
|
+
include example error responses
|
|
2208
|
+
|
|
2209
|
+
[3] ALWAYS PROVIDE EXAMPLES
|
|
2210
|
+
at minimum: curl example
|
|
2211
|
+
better: curl + one language example
|
|
2212
|
+
best: curl + 2+ language examples
|
|
2213
|
+
|
|
2214
|
+
[4] VALIDATE OPENAPI SPECS
|
|
2215
|
+
before committing, validate:
|
|
2216
|
+
<terminal>swagger-cli validate openapi.yaml</terminal>
|
|
2217
|
+
|
|
2218
|
+
[5] TEST YOUR EXAMPLES
|
|
2219
|
+
every code example must be tested
|
|
2220
|
+
if you can't run it, don't include it
|
|
2221
|
+
|
|
2222
|
+
[6] DOCUMENT PARAMETERS FULLY
|
|
2223
|
+
name, type, required/optional, description, example, constraints
|
|
2224
|
+
no parameter should be partially documented
|
|
2225
|
+
|
|
2226
|
+
[7] INCLUDE AUTHENTICATION
|
|
2227
|
+
every endpoint must show auth requirements
|
|
2228
|
+
if no auth needed, explicitly state it
|
|
2229
|
+
|
|
2230
|
+
[8] MAINTAIN CHANGELOG
|
|
2231
|
+
every API change goes in the changelog
|
|
2232
|
+
no silent changes
|
|
2233
|
+
|
|
2234
|
+
[9] PROVIDE MIGRATION GUIDES
|
|
2235
|
+
for any breaking change, provide migration guide
|
|
2236
|
+
deprecation notice at least 6 months before removal
|
|
2237
|
+
|
|
2238
|
+
[10] THINK FROM USER PERSPECTIVE
|
|
2239
|
+
would a new developer understand this?
|
|
2240
|
+
would a non-technical stakeholder understand?
|
|
2241
|
+
if not, rewrite
|
|
2242
|
+
|
|
2243
|
+
|
|
2244
|
+
PHASE 16: DOCUMENTATION MAINTENANCE
|
|
2245
|
+
|
|
2246
|
+
|
|
2247
|
+
keeping docs current
|
|
2248
|
+
|
|
2249
|
+
[ ] review schedule
|
|
2250
|
+
weekly: check for new endpoints
|
|
2251
|
+
monthly: full documentation review
|
|
2252
|
+
quarterly: user feedback review
|
|
2253
|
+
|
|
2254
|
+
[ ] automated checks
|
|
2255
|
+
- openapi spec validation in ci
|
|
2256
|
+
- example testing in ci
|
|
2257
|
+
- link checking
|
|
2258
|
+
|
|
2259
|
+
[ ] feedback loop
|
|
2260
|
+
- add feedback widget to docs
|
|
2261
|
+
- track documentation issues
|
|
2262
|
+
- survey users annually
|
|
2263
|
+
|
|
2264
|
+
[ ] version control
|
|
2265
|
+
- tag docs with API version
|
|
2266
|
+
- keep old versions accessible
|
|
2267
|
+
- maintain docs in same repo as code
|
|
2268
|
+
|
|
2269
|
+
example CI check:
|
|
2270
|
+
|
|
2271
|
+
```yaml
|
|
2272
|
+
# .github/workflows/docs-check.yml
|
|
2273
|
+
name: Documentation Checks
|
|
2274
|
+
|
|
2275
|
+
on: [pull_request]
|
|
2276
|
+
|
|
2277
|
+
jobs:
|
|
2278
|
+
validate-openapi:
|
|
2279
|
+
runs-on: ubuntu-latest
|
|
2280
|
+
steps:
|
|
2281
|
+
- uses: actions/checkout@v3
|
|
2282
|
+
- name: Validate OpenAPI spec
|
|
2283
|
+
run: |
|
|
2284
|
+
npm install -g @apidevtools/swagger-cli
|
|
2285
|
+
swagger-cli validate openapi.yaml
|
|
2286
|
+
|
|
2287
|
+
test-examples:
|
|
2288
|
+
runs-on: ubuntu-latest
|
|
2289
|
+
steps:
|
|
2290
|
+
- uses: actions/checkout@v3
|
|
2291
|
+
- name: Test curl examples
|
|
2292
|
+
run: |
|
|
2293
|
+
# Extract and test curl commands from docs
|
|
2294
|
+
./scripts/test-examples.sh
|
|
2295
|
+
```
|
|
2296
|
+
|
|
2297
|
+
|
|
2298
|
+
FINAL REMINDERS
|
|
2299
|
+
|
|
2300
|
+
|
|
2301
|
+
documentation is a product interface
|
|
2302
|
+
|
|
2303
|
+
the API docs are often the first thing developers see.
|
|
2304
|
+
poor documentation = poor developer experience.
|
|
2305
|
+
excellent documentation = happy developers, fewer support tickets.
|
|
2306
|
+
|
|
2307
|
+
|
|
2308
|
+
docs are never done
|
|
2309
|
+
|
|
2310
|
+
APIs evolve.
|
|
2311
|
+
documentation must evolve with them.
|
|
2312
|
+
make docs part of your development process.
|
|
2313
|
+
|
|
2314
|
+
|
|
2315
|
+
the rule of clarity
|
|
2316
|
+
|
|
2317
|
+
if you have to read it twice, rewrite it.
|
|
2318
|
+
if you have to explain it, it's not clear.
|
|
2319
|
+
simple, direct language wins.
|
|
2320
|
+
|
|
2321
|
+
|
|
2322
|
+
when in doubt
|
|
2323
|
+
|
|
2324
|
+
add an example.
|
|
2325
|
+
examples bridge the gap between abstract and concrete.
|
|
2326
|
+
a good example is worth a thousand descriptions.
|
|
2327
|
+
|
|
2328
|
+
now go document every endpoint.
|