@nasebanal/cli 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +237 -0
- package/dist/apis.js +6 -0
- package/dist/auth.js +98 -0
- package/dist/config.js +97 -0
- package/dist/http.js +55 -0
- package/dist/index.js +53 -0
- package/dist/oauth.js +265 -0
- package/dist/output.js +26 -0
- package/dist/spec/build.js +185 -0
- package/dist/spec/loader.js +23 -0
- package/package.json +59 -0
- package/specs/account.yaml +990 -0
- package/specs/app-template.yaml +486 -0
- package/specs/recorder.yaml +2774 -0
- package/specs/target.yaml +1540 -0
|
@@ -0,0 +1,2774 @@
|
|
|
1
|
+
openapi: 3.0.3
|
|
2
|
+
info:
|
|
3
|
+
title: NASEBANAL Recorder API
|
|
4
|
+
description: >-
|
|
5
|
+
Time-series data recorder API built with Cloudflare Workers and D1.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Authentication
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Create a Personal Access Token (PAT) in **Profile Settings > API Tokens** (requires Pro plan or above), then include
|
|
12
|
+
it in the `Authorization` header.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Tokens use the format `nbpat_` followed by 40 random characters.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
**Usage:**
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Authorization: Bearer nbpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
**curl example:**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
|
|
31
|
+
curl -H "Authorization: Bearer nbpat_xxxxx..." https://api.example.com/api/v1/records
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
**Plan limits:**
|
|
37
|
+
|
|
38
|
+
- Free: API tokens not available
|
|
39
|
+
|
|
40
|
+
- Pro: up to 5 tokens
|
|
41
|
+
|
|
42
|
+
- Family: up to 20 tokens
|
|
43
|
+
|
|
44
|
+
- Enterprise: unlimited tokens
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## SNS Shared Graph Embed
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
Publicly shared graphs can be embedded in external sites (blogs, portfolios, etc.) via iframe without
|
|
51
|
+
authentication.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
**Endpoint:** `GET /shared/{lang}/{id}/embed`
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
| Parameter | Type | Required | Description |
|
|
58
|
+
|
|
59
|
+
|-----------|------|----------|-------------|
|
|
60
|
+
|
|
61
|
+
| `lang` | path | Yes | Language: `ja` or `en` |
|
|
62
|
+
|
|
63
|
+
| `id` | path | Yes | Shared graph ID |
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
**Features:** Line chart with auto-scale, goal lines, statistics. No navigation header or footer — designed for
|
|
67
|
+
iframe embedding.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
**Auth:** Not required (public access).
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
**iframe example:**
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
|
|
77
|
+
<iframe src=\"https://recorder.nasebanal.com/shared/ja/{id}/embed\" width=\"100%\" height=\"500\"
|
|
78
|
+
frameborder=\"0\"></iframe>
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
**Note:** The embed displays a snapshot of the data at the time the SNS share was created. To update the data,
|
|
84
|
+
recreate the SNS share.
|
|
85
|
+
version: 1.5.0
|
|
86
|
+
license:
|
|
87
|
+
name: MIT
|
|
88
|
+
servers:
|
|
89
|
+
- url: http://localhost:8787/api/v1
|
|
90
|
+
description: Local dev (wrangler dev)
|
|
91
|
+
- url: https://api.recorder.nasebanal.com/api/v1
|
|
92
|
+
description: Production
|
|
93
|
+
security:
|
|
94
|
+
- BearerAuth: []
|
|
95
|
+
paths:
|
|
96
|
+
/health:
|
|
97
|
+
get:
|
|
98
|
+
summary: Health check
|
|
99
|
+
description: Check if the API is running
|
|
100
|
+
operationId: getHealth
|
|
101
|
+
tags:
|
|
102
|
+
- Health
|
|
103
|
+
# Public — the health check is unauthenticated in the implementation.
|
|
104
|
+
# Override the global BearerAuth requirement so it stays open.
|
|
105
|
+
security: []
|
|
106
|
+
responses:
|
|
107
|
+
'200':
|
|
108
|
+
description: API is healthy
|
|
109
|
+
content:
|
|
110
|
+
application/json:
|
|
111
|
+
schema:
|
|
112
|
+
type: object
|
|
113
|
+
properties:
|
|
114
|
+
success:
|
|
115
|
+
type: boolean
|
|
116
|
+
enum:
|
|
117
|
+
- true
|
|
118
|
+
example: true
|
|
119
|
+
data:
|
|
120
|
+
type: object
|
|
121
|
+
properties:
|
|
122
|
+
status:
|
|
123
|
+
type: string
|
|
124
|
+
example: ok
|
|
125
|
+
timestamp:
|
|
126
|
+
type: string
|
|
127
|
+
format: date-time
|
|
128
|
+
/me:
|
|
129
|
+
get:
|
|
130
|
+
summary: Get current user profile
|
|
131
|
+
description: Retrieve the authenticated user's profile data
|
|
132
|
+
operationId: getCurrentUser
|
|
133
|
+
tags:
|
|
134
|
+
- User
|
|
135
|
+
responses:
|
|
136
|
+
'200':
|
|
137
|
+
description: Current user profile
|
|
138
|
+
content:
|
|
139
|
+
application/json:
|
|
140
|
+
schema:
|
|
141
|
+
type: object
|
|
142
|
+
properties:
|
|
143
|
+
success:
|
|
144
|
+
type: boolean
|
|
145
|
+
enum:
|
|
146
|
+
- true
|
|
147
|
+
example: true
|
|
148
|
+
data:
|
|
149
|
+
$ref: '#/components/schemas/User'
|
|
150
|
+
'401':
|
|
151
|
+
$ref: '#/components/responses/Unauthorized'
|
|
152
|
+
'500':
|
|
153
|
+
$ref: '#/components/responses/InternalError'
|
|
154
|
+
/records:
|
|
155
|
+
get:
|
|
156
|
+
summary: Get all records
|
|
157
|
+
description: Retrieve all records with optional filtering
|
|
158
|
+
operationId: getRecords
|
|
159
|
+
tags:
|
|
160
|
+
- Records
|
|
161
|
+
parameters:
|
|
162
|
+
- name: name
|
|
163
|
+
in: query
|
|
164
|
+
description: Filter by record name
|
|
165
|
+
schema:
|
|
166
|
+
type: string
|
|
167
|
+
- name: start_date
|
|
168
|
+
in: query
|
|
169
|
+
description: Filter by start date (ISO 8601)
|
|
170
|
+
schema:
|
|
171
|
+
type: string
|
|
172
|
+
format: date-time
|
|
173
|
+
- name: end_date
|
|
174
|
+
in: query
|
|
175
|
+
description: Filter by end date (ISO 8601)
|
|
176
|
+
schema:
|
|
177
|
+
type: string
|
|
178
|
+
format: date-time
|
|
179
|
+
- name: limit
|
|
180
|
+
in: query
|
|
181
|
+
description: 'Maximum number of records to return for unbounded queries (max: 1000). When start_date or end_date is supplied, all records in the range are returned — the date filter itself bounds the payload.'
|
|
182
|
+
schema:
|
|
183
|
+
type: integer
|
|
184
|
+
default: 100
|
|
185
|
+
maximum: 1000
|
|
186
|
+
- name: offset
|
|
187
|
+
in: query
|
|
188
|
+
description: Number of records to skip
|
|
189
|
+
schema:
|
|
190
|
+
type: integer
|
|
191
|
+
default: 0
|
|
192
|
+
responses:
|
|
193
|
+
'200':
|
|
194
|
+
description: List of records
|
|
195
|
+
content:
|
|
196
|
+
application/json:
|
|
197
|
+
schema:
|
|
198
|
+
$ref: '#/components/schemas/RecordListResponse'
|
|
199
|
+
'500':
|
|
200
|
+
$ref: '#/components/responses/InternalError'
|
|
201
|
+
post:
|
|
202
|
+
summary: Create a new record
|
|
203
|
+
description: Create a new time-series record
|
|
204
|
+
operationId: createRecord
|
|
205
|
+
tags:
|
|
206
|
+
- Records
|
|
207
|
+
requestBody:
|
|
208
|
+
required: true
|
|
209
|
+
content:
|
|
210
|
+
application/json:
|
|
211
|
+
schema:
|
|
212
|
+
$ref: '#/components/schemas/CreateRecordInput'
|
|
213
|
+
responses:
|
|
214
|
+
'201':
|
|
215
|
+
description: Record created successfully
|
|
216
|
+
content:
|
|
217
|
+
application/json:
|
|
218
|
+
schema:
|
|
219
|
+
$ref: '#/components/schemas/RecordResponse'
|
|
220
|
+
'400':
|
|
221
|
+
$ref: '#/components/responses/ValidationError'
|
|
222
|
+
'500':
|
|
223
|
+
$ref: '#/components/responses/InternalError'
|
|
224
|
+
/records/{id}:
|
|
225
|
+
get:
|
|
226
|
+
summary: Get a single record
|
|
227
|
+
description: Retrieve a record by ID
|
|
228
|
+
operationId: getRecord
|
|
229
|
+
tags:
|
|
230
|
+
- Records
|
|
231
|
+
parameters:
|
|
232
|
+
- $ref: '#/components/parameters/RecordId'
|
|
233
|
+
responses:
|
|
234
|
+
'200':
|
|
235
|
+
description: Record details
|
|
236
|
+
content:
|
|
237
|
+
application/json:
|
|
238
|
+
schema:
|
|
239
|
+
$ref: '#/components/schemas/RecordResponse'
|
|
240
|
+
'404':
|
|
241
|
+
$ref: '#/components/responses/NotFound'
|
|
242
|
+
'500':
|
|
243
|
+
$ref: '#/components/responses/InternalError'
|
|
244
|
+
put:
|
|
245
|
+
summary: Update a record
|
|
246
|
+
description: Update an existing record by ID
|
|
247
|
+
operationId: updateRecord
|
|
248
|
+
tags:
|
|
249
|
+
- Records
|
|
250
|
+
parameters:
|
|
251
|
+
- $ref: '#/components/parameters/RecordId'
|
|
252
|
+
requestBody:
|
|
253
|
+
required: true
|
|
254
|
+
content:
|
|
255
|
+
application/json:
|
|
256
|
+
schema:
|
|
257
|
+
$ref: '#/components/schemas/UpdateRecordInput'
|
|
258
|
+
responses:
|
|
259
|
+
'200':
|
|
260
|
+
description: Record updated successfully
|
|
261
|
+
content:
|
|
262
|
+
application/json:
|
|
263
|
+
schema:
|
|
264
|
+
$ref: '#/components/schemas/RecordResponse'
|
|
265
|
+
'400':
|
|
266
|
+
$ref: '#/components/responses/ValidationError'
|
|
267
|
+
'404':
|
|
268
|
+
$ref: '#/components/responses/NotFound'
|
|
269
|
+
'500':
|
|
270
|
+
$ref: '#/components/responses/InternalError'
|
|
271
|
+
delete:
|
|
272
|
+
summary: Delete a record
|
|
273
|
+
description: Delete a record by ID
|
|
274
|
+
operationId: deleteRecord
|
|
275
|
+
tags:
|
|
276
|
+
- Records
|
|
277
|
+
parameters:
|
|
278
|
+
- $ref: '#/components/parameters/RecordId'
|
|
279
|
+
responses:
|
|
280
|
+
'200':
|
|
281
|
+
description: Record deleted successfully
|
|
282
|
+
content:
|
|
283
|
+
application/json:
|
|
284
|
+
schema:
|
|
285
|
+
type: object
|
|
286
|
+
properties:
|
|
287
|
+
success:
|
|
288
|
+
type: boolean
|
|
289
|
+
enum:
|
|
290
|
+
- true
|
|
291
|
+
example: true
|
|
292
|
+
data:
|
|
293
|
+
type: object
|
|
294
|
+
properties:
|
|
295
|
+
success:
|
|
296
|
+
type: boolean
|
|
297
|
+
enum:
|
|
298
|
+
- true
|
|
299
|
+
example: true
|
|
300
|
+
'404':
|
|
301
|
+
$ref: '#/components/responses/NotFound'
|
|
302
|
+
'500':
|
|
303
|
+
$ref: '#/components/responses/InternalError'
|
|
304
|
+
/records/{id}/visibility:
|
|
305
|
+
patch:
|
|
306
|
+
summary: Toggle record visibility
|
|
307
|
+
description: Show or hide a record
|
|
308
|
+
operationId: toggleRecordVisibility
|
|
309
|
+
tags:
|
|
310
|
+
- Records
|
|
311
|
+
parameters:
|
|
312
|
+
- $ref: '#/components/parameters/RecordId'
|
|
313
|
+
requestBody:
|
|
314
|
+
required: true
|
|
315
|
+
content:
|
|
316
|
+
application/json:
|
|
317
|
+
schema:
|
|
318
|
+
type: object
|
|
319
|
+
properties:
|
|
320
|
+
hide:
|
|
321
|
+
type: boolean
|
|
322
|
+
required:
|
|
323
|
+
- hide
|
|
324
|
+
responses:
|
|
325
|
+
'200':
|
|
326
|
+
description: Visibility updated
|
|
327
|
+
content:
|
|
328
|
+
application/json:
|
|
329
|
+
schema:
|
|
330
|
+
type: object
|
|
331
|
+
properties:
|
|
332
|
+
success:
|
|
333
|
+
type: boolean
|
|
334
|
+
enum:
|
|
335
|
+
- true
|
|
336
|
+
example: true
|
|
337
|
+
data:
|
|
338
|
+
type: object
|
|
339
|
+
properties:
|
|
340
|
+
id:
|
|
341
|
+
type: integer
|
|
342
|
+
is_hidden:
|
|
343
|
+
type: integer
|
|
344
|
+
description: 0 = visible, 1 = hidden
|
|
345
|
+
'404':
|
|
346
|
+
$ref: '#/components/responses/NotFound'
|
|
347
|
+
'500':
|
|
348
|
+
$ref: '#/components/responses/InternalError'
|
|
349
|
+
/records/bulk-delete:
|
|
350
|
+
post:
|
|
351
|
+
summary: Bulk delete records
|
|
352
|
+
description: Delete multiple records at once by providing an array of IDs
|
|
353
|
+
operationId: bulkDeleteRecords
|
|
354
|
+
tags:
|
|
355
|
+
- Records
|
|
356
|
+
requestBody:
|
|
357
|
+
required: true
|
|
358
|
+
content:
|
|
359
|
+
application/json:
|
|
360
|
+
schema:
|
|
361
|
+
type: object
|
|
362
|
+
properties:
|
|
363
|
+
ids:
|
|
364
|
+
type: array
|
|
365
|
+
items:
|
|
366
|
+
type: integer
|
|
367
|
+
description: Array of record IDs to delete
|
|
368
|
+
example:
|
|
369
|
+
- 1
|
|
370
|
+
- 2
|
|
371
|
+
- 3
|
|
372
|
+
required:
|
|
373
|
+
- ids
|
|
374
|
+
responses:
|
|
375
|
+
'200':
|
|
376
|
+
description: Records deleted successfully
|
|
377
|
+
content:
|
|
378
|
+
application/json:
|
|
379
|
+
schema:
|
|
380
|
+
type: object
|
|
381
|
+
properties:
|
|
382
|
+
success:
|
|
383
|
+
type: boolean
|
|
384
|
+
enum:
|
|
385
|
+
- true
|
|
386
|
+
example: true
|
|
387
|
+
data:
|
|
388
|
+
type: object
|
|
389
|
+
properties:
|
|
390
|
+
deleted:
|
|
391
|
+
type: integer
|
|
392
|
+
description: Number of records deleted
|
|
393
|
+
'400':
|
|
394
|
+
$ref: '#/components/responses/ValidationError'
|
|
395
|
+
'500':
|
|
396
|
+
$ref: '#/components/responses/InternalError'
|
|
397
|
+
/tags:
|
|
398
|
+
get:
|
|
399
|
+
summary: Get all tags
|
|
400
|
+
description: Retrieve all tags for the authenticated user
|
|
401
|
+
operationId: getTags
|
|
402
|
+
tags:
|
|
403
|
+
- Tags
|
|
404
|
+
responses:
|
|
405
|
+
'200':
|
|
406
|
+
description: List of tags
|
|
407
|
+
content:
|
|
408
|
+
application/json:
|
|
409
|
+
schema:
|
|
410
|
+
type: object
|
|
411
|
+
properties:
|
|
412
|
+
success:
|
|
413
|
+
type: boolean
|
|
414
|
+
enum:
|
|
415
|
+
- true
|
|
416
|
+
example: true
|
|
417
|
+
data:
|
|
418
|
+
type: object
|
|
419
|
+
properties:
|
|
420
|
+
data:
|
|
421
|
+
type: array
|
|
422
|
+
items:
|
|
423
|
+
$ref: '#/components/schemas/Tag'
|
|
424
|
+
total:
|
|
425
|
+
type: integer
|
|
426
|
+
'500':
|
|
427
|
+
$ref: '#/components/responses/InternalError'
|
|
428
|
+
/tags/{id}:
|
|
429
|
+
put:
|
|
430
|
+
summary: Update a tag
|
|
431
|
+
description: Update an existing tag by ID
|
|
432
|
+
operationId: updateTag
|
|
433
|
+
tags:
|
|
434
|
+
- Tags
|
|
435
|
+
parameters:
|
|
436
|
+
- name: id
|
|
437
|
+
in: path
|
|
438
|
+
required: true
|
|
439
|
+
schema:
|
|
440
|
+
type: integer
|
|
441
|
+
minimum: 1
|
|
442
|
+
requestBody:
|
|
443
|
+
required: true
|
|
444
|
+
content:
|
|
445
|
+
application/json:
|
|
446
|
+
schema:
|
|
447
|
+
type: object
|
|
448
|
+
properties:
|
|
449
|
+
name:
|
|
450
|
+
type: string
|
|
451
|
+
unit:
|
|
452
|
+
type: string
|
|
453
|
+
nullable: true
|
|
454
|
+
description: >-
|
|
455
|
+
Unit of measurement. For a `time`-typed tag, must be
|
|
456
|
+
`hour`, `minute`, or `second`, controlling whether the
|
|
457
|
+
value formats as H:MM:SS, MM:SS, or decimal seconds.
|
|
458
|
+
chart_type:
|
|
459
|
+
type: string
|
|
460
|
+
enum:
|
|
461
|
+
- line
|
|
462
|
+
- smooth
|
|
463
|
+
- step
|
|
464
|
+
data_type:
|
|
465
|
+
type: string
|
|
466
|
+
description: >-
|
|
467
|
+
The scale every stored value is written against (a `time` tag's 90 means 90
|
|
468
|
+
minutes). Changing it does NOT convert existing values, so it is
|
|
469
|
+
accepted only when no stored value would change meaning: widening
|
|
470
|
+
(`integer` → `decimal`) is always allowed, narrowing only while every
|
|
471
|
+
stored value is representable in the new type, and moving into or out of
|
|
472
|
+
`time` never is once the tag holds data. Otherwise the request is refused
|
|
473
|
+
with 409 `TAG_HAS_DATA`, whose `error.details.reason` carries the same
|
|
474
|
+
code as `Tag.data_type_options[].reason`. Read `data_type_options` off
|
|
475
|
+
`GET /tags` rather than re-deriving this. Sending the tag's current value
|
|
476
|
+
is not a change and is always accepted.
|
|
477
|
+
enum:
|
|
478
|
+
- binary
|
|
479
|
+
- decimal
|
|
480
|
+
- integer
|
|
481
|
+
- time
|
|
482
|
+
responses:
|
|
483
|
+
'200':
|
|
484
|
+
description: Tag updated successfully
|
|
485
|
+
content:
|
|
486
|
+
application/json:
|
|
487
|
+
schema:
|
|
488
|
+
type: object
|
|
489
|
+
properties:
|
|
490
|
+
success:
|
|
491
|
+
type: boolean
|
|
492
|
+
enum:
|
|
493
|
+
- true
|
|
494
|
+
example: true
|
|
495
|
+
data:
|
|
496
|
+
$ref: '#/components/schemas/Tag'
|
|
497
|
+
'404':
|
|
498
|
+
$ref: '#/components/responses/NotFound'
|
|
499
|
+
'409':
|
|
500
|
+
description: >-
|
|
501
|
+
Refused: the requested `data_type` would change the meaning of values
|
|
502
|
+
already stored. `error.details.reason` is one of `SCALE_CHANGE`,
|
|
503
|
+
`NON_INTEGER_VALUES`, `NON_BINARY_VALUES`.
|
|
504
|
+
content:
|
|
505
|
+
application/json:
|
|
506
|
+
schema:
|
|
507
|
+
$ref: '#/components/schemas/ErrorResponse'
|
|
508
|
+
example:
|
|
509
|
+
success: false
|
|
510
|
+
error:
|
|
511
|
+
code: TAG_HAS_DATA
|
|
512
|
+
message: >-
|
|
513
|
+
Cannot change data_type: this tag already has records or
|
|
514
|
+
goals written against its current scale.
|
|
515
|
+
'500':
|
|
516
|
+
$ref: '#/components/responses/InternalError'
|
|
517
|
+
delete:
|
|
518
|
+
summary: Delete a tag
|
|
519
|
+
description: Delete a tag and all associated records by ID
|
|
520
|
+
operationId: deleteTag
|
|
521
|
+
tags:
|
|
522
|
+
- Tags
|
|
523
|
+
parameters:
|
|
524
|
+
- name: id
|
|
525
|
+
in: path
|
|
526
|
+
required: true
|
|
527
|
+
schema:
|
|
528
|
+
type: integer
|
|
529
|
+
minimum: 1
|
|
530
|
+
responses:
|
|
531
|
+
'200':
|
|
532
|
+
description: Tag deleted successfully
|
|
533
|
+
content:
|
|
534
|
+
application/json:
|
|
535
|
+
schema:
|
|
536
|
+
type: object
|
|
537
|
+
properties:
|
|
538
|
+
success:
|
|
539
|
+
type: boolean
|
|
540
|
+
enum:
|
|
541
|
+
- true
|
|
542
|
+
example: true
|
|
543
|
+
data:
|
|
544
|
+
type: object
|
|
545
|
+
properties:
|
|
546
|
+
success:
|
|
547
|
+
type: boolean
|
|
548
|
+
enum:
|
|
549
|
+
- true
|
|
550
|
+
example: true
|
|
551
|
+
'404':
|
|
552
|
+
$ref: '#/components/responses/NotFound'
|
|
553
|
+
'500':
|
|
554
|
+
$ref: '#/components/responses/InternalError'
|
|
555
|
+
/tags/{id}/visibility:
|
|
556
|
+
patch:
|
|
557
|
+
summary: Toggle tag visibility
|
|
558
|
+
description: Show or hide a tag
|
|
559
|
+
operationId: toggleTagVisibility
|
|
560
|
+
tags:
|
|
561
|
+
- Tags
|
|
562
|
+
parameters:
|
|
563
|
+
- name: id
|
|
564
|
+
in: path
|
|
565
|
+
required: true
|
|
566
|
+
schema:
|
|
567
|
+
type: integer
|
|
568
|
+
minimum: 1
|
|
569
|
+
requestBody:
|
|
570
|
+
required: true
|
|
571
|
+
content:
|
|
572
|
+
application/json:
|
|
573
|
+
schema:
|
|
574
|
+
type: object
|
|
575
|
+
properties:
|
|
576
|
+
hide:
|
|
577
|
+
type: boolean
|
|
578
|
+
required:
|
|
579
|
+
- hide
|
|
580
|
+
responses:
|
|
581
|
+
'200':
|
|
582
|
+
description: Visibility updated
|
|
583
|
+
content:
|
|
584
|
+
application/json:
|
|
585
|
+
schema:
|
|
586
|
+
type: object
|
|
587
|
+
properties:
|
|
588
|
+
success:
|
|
589
|
+
type: boolean
|
|
590
|
+
enum:
|
|
591
|
+
- true
|
|
592
|
+
example: true
|
|
593
|
+
data:
|
|
594
|
+
$ref: '#/components/schemas/Tag'
|
|
595
|
+
'404':
|
|
596
|
+
$ref: '#/components/responses/NotFound'
|
|
597
|
+
'500':
|
|
598
|
+
$ref: '#/components/responses/InternalError'
|
|
599
|
+
/tags/bulk-delete:
|
|
600
|
+
post:
|
|
601
|
+
summary: Bulk delete tags
|
|
602
|
+
description: Delete multiple tags and their associated records at once
|
|
603
|
+
operationId: bulkDeleteTags
|
|
604
|
+
tags:
|
|
605
|
+
- Tags
|
|
606
|
+
requestBody:
|
|
607
|
+
required: true
|
|
608
|
+
content:
|
|
609
|
+
application/json:
|
|
610
|
+
schema:
|
|
611
|
+
type: object
|
|
612
|
+
properties:
|
|
613
|
+
ids:
|
|
614
|
+
type: array
|
|
615
|
+
items:
|
|
616
|
+
type: integer
|
|
617
|
+
description: Array of tag IDs to delete
|
|
618
|
+
example:
|
|
619
|
+
- 1
|
|
620
|
+
- 2
|
|
621
|
+
- 3
|
|
622
|
+
required:
|
|
623
|
+
- ids
|
|
624
|
+
responses:
|
|
625
|
+
'200':
|
|
626
|
+
description: Tags deleted successfully
|
|
627
|
+
content:
|
|
628
|
+
application/json:
|
|
629
|
+
schema:
|
|
630
|
+
type: object
|
|
631
|
+
properties:
|
|
632
|
+
success:
|
|
633
|
+
type: boolean
|
|
634
|
+
enum:
|
|
635
|
+
- true
|
|
636
|
+
example: true
|
|
637
|
+
data:
|
|
638
|
+
type: object
|
|
639
|
+
properties:
|
|
640
|
+
deleted:
|
|
641
|
+
type: integer
|
|
642
|
+
description: Number of tags deleted
|
|
643
|
+
'400':
|
|
644
|
+
$ref: '#/components/responses/ValidationError'
|
|
645
|
+
'500':
|
|
646
|
+
$ref: '#/components/responses/InternalError'
|
|
647
|
+
/shares:
|
|
648
|
+
get:
|
|
649
|
+
summary: Get my shares
|
|
650
|
+
description: Retrieve shares created by the authenticated user
|
|
651
|
+
operationId: getMyShares
|
|
652
|
+
tags:
|
|
653
|
+
- Shares
|
|
654
|
+
responses:
|
|
655
|
+
'200':
|
|
656
|
+
description: List of shares
|
|
657
|
+
content:
|
|
658
|
+
application/json:
|
|
659
|
+
schema:
|
|
660
|
+
type: object
|
|
661
|
+
properties:
|
|
662
|
+
success:
|
|
663
|
+
type: boolean
|
|
664
|
+
enum:
|
|
665
|
+
- true
|
|
666
|
+
example: true
|
|
667
|
+
data:
|
|
668
|
+
type: object
|
|
669
|
+
properties:
|
|
670
|
+
data:
|
|
671
|
+
type: array
|
|
672
|
+
items:
|
|
673
|
+
$ref: '#/components/schemas/Share'
|
|
674
|
+
total:
|
|
675
|
+
type: integer
|
|
676
|
+
'500':
|
|
677
|
+
$ref: '#/components/responses/InternalError'
|
|
678
|
+
post:
|
|
679
|
+
summary: Create a share (sends approval email)
|
|
680
|
+
description: >-
|
|
681
|
+
Create a tag share addressed to another user by email. The share is created in status `pending` and does NOT
|
|
682
|
+
grant data access until the recipient clicks the approval link in the invitation email delivered via the Account
|
|
683
|
+
API (Resend). Invitations expire after 7 days; after expiry the recipient must be re-invited. The raw invitation
|
|
684
|
+
token is never stored — only a SHA-256 hash — so even database access does not expose reusable accept links.
|
|
685
|
+
operationId: createShare
|
|
686
|
+
tags:
|
|
687
|
+
- Shares
|
|
688
|
+
requestBody:
|
|
689
|
+
required: true
|
|
690
|
+
content:
|
|
691
|
+
application/json:
|
|
692
|
+
schema:
|
|
693
|
+
type: object
|
|
694
|
+
properties:
|
|
695
|
+
shared_with_email:
|
|
696
|
+
type: string
|
|
697
|
+
format: email
|
|
698
|
+
tag_id:
|
|
699
|
+
type: integer
|
|
700
|
+
nullable: true
|
|
701
|
+
permission:
|
|
702
|
+
type: string
|
|
703
|
+
enum:
|
|
704
|
+
- read
|
|
705
|
+
- write
|
|
706
|
+
required:
|
|
707
|
+
- shared_with_email
|
|
708
|
+
- permission
|
|
709
|
+
responses:
|
|
710
|
+
'201':
|
|
711
|
+
description: Share created successfully
|
|
712
|
+
content:
|
|
713
|
+
application/json:
|
|
714
|
+
schema:
|
|
715
|
+
type: object
|
|
716
|
+
properties:
|
|
717
|
+
success:
|
|
718
|
+
type: boolean
|
|
719
|
+
enum:
|
|
720
|
+
- true
|
|
721
|
+
example: true
|
|
722
|
+
data:
|
|
723
|
+
$ref: '#/components/schemas/Share'
|
|
724
|
+
'400':
|
|
725
|
+
$ref: '#/components/responses/ValidationError'
|
|
726
|
+
'500':
|
|
727
|
+
$ref: '#/components/responses/InternalError'
|
|
728
|
+
/shares/with-me:
|
|
729
|
+
get:
|
|
730
|
+
summary: Get shares shared with me
|
|
731
|
+
description: >-
|
|
732
|
+
Retrieve shares that other users have shared with the authenticated user. Returns only `accepted` shares by
|
|
733
|
+
default. Pass `?status=pending` to list invitations awaiting approval, `?status=rejected` for declined
|
|
734
|
+
invitations, or `?status=all` for every row.
|
|
735
|
+
operationId: getSharedWithMe
|
|
736
|
+
tags:
|
|
737
|
+
- Shares
|
|
738
|
+
parameters:
|
|
739
|
+
- name: status
|
|
740
|
+
in: query
|
|
741
|
+
required: false
|
|
742
|
+
schema:
|
|
743
|
+
type: string
|
|
744
|
+
enum:
|
|
745
|
+
- accepted
|
|
746
|
+
- pending
|
|
747
|
+
- rejected
|
|
748
|
+
- all
|
|
749
|
+
default: accepted
|
|
750
|
+
description: Filter by approval status.
|
|
751
|
+
responses:
|
|
752
|
+
'200':
|
|
753
|
+
description: List of shares shared with the user
|
|
754
|
+
content:
|
|
755
|
+
application/json:
|
|
756
|
+
schema:
|
|
757
|
+
type: object
|
|
758
|
+
properties:
|
|
759
|
+
success:
|
|
760
|
+
type: boolean
|
|
761
|
+
enum:
|
|
762
|
+
- true
|
|
763
|
+
example: true
|
|
764
|
+
data:
|
|
765
|
+
type: object
|
|
766
|
+
properties:
|
|
767
|
+
data:
|
|
768
|
+
type: array
|
|
769
|
+
items:
|
|
770
|
+
$ref: '#/components/schemas/Share'
|
|
771
|
+
total:
|
|
772
|
+
type: integer
|
|
773
|
+
'500':
|
|
774
|
+
$ref: '#/components/responses/InternalError'
|
|
775
|
+
/shares/{id}:
|
|
776
|
+
put:
|
|
777
|
+
summary: Update a share
|
|
778
|
+
description: Update an existing share
|
|
779
|
+
operationId: updateShare
|
|
780
|
+
tags:
|
|
781
|
+
- Shares
|
|
782
|
+
parameters:
|
|
783
|
+
- name: id
|
|
784
|
+
in: path
|
|
785
|
+
required: true
|
|
786
|
+
schema:
|
|
787
|
+
type: integer
|
|
788
|
+
minimum: 1
|
|
789
|
+
requestBody:
|
|
790
|
+
required: true
|
|
791
|
+
content:
|
|
792
|
+
application/json:
|
|
793
|
+
schema:
|
|
794
|
+
type: object
|
|
795
|
+
properties:
|
|
796
|
+
tag_id:
|
|
797
|
+
type: integer
|
|
798
|
+
permission:
|
|
799
|
+
type: string
|
|
800
|
+
enum:
|
|
801
|
+
- read
|
|
802
|
+
- write
|
|
803
|
+
required:
|
|
804
|
+
- tag_id
|
|
805
|
+
- permission
|
|
806
|
+
responses:
|
|
807
|
+
'200':
|
|
808
|
+
description: Share updated
|
|
809
|
+
content:
|
|
810
|
+
application/json:
|
|
811
|
+
schema:
|
|
812
|
+
type: object
|
|
813
|
+
properties:
|
|
814
|
+
success:
|
|
815
|
+
type: boolean
|
|
816
|
+
enum:
|
|
817
|
+
- true
|
|
818
|
+
example: true
|
|
819
|
+
data:
|
|
820
|
+
$ref: '#/components/schemas/Share'
|
|
821
|
+
'404':
|
|
822
|
+
$ref: '#/components/responses/NotFound'
|
|
823
|
+
'500':
|
|
824
|
+
$ref: '#/components/responses/InternalError'
|
|
825
|
+
delete:
|
|
826
|
+
summary: Delete a share
|
|
827
|
+
description: Delete a share by ID
|
|
828
|
+
operationId: deleteShare
|
|
829
|
+
tags:
|
|
830
|
+
- Shares
|
|
831
|
+
parameters:
|
|
832
|
+
- name: id
|
|
833
|
+
in: path
|
|
834
|
+
required: true
|
|
835
|
+
schema:
|
|
836
|
+
type: integer
|
|
837
|
+
minimum: 1
|
|
838
|
+
responses:
|
|
839
|
+
'200':
|
|
840
|
+
description: Share deleted
|
|
841
|
+
content:
|
|
842
|
+
application/json:
|
|
843
|
+
schema:
|
|
844
|
+
type: object
|
|
845
|
+
properties:
|
|
846
|
+
success:
|
|
847
|
+
type: boolean
|
|
848
|
+
enum:
|
|
849
|
+
- true
|
|
850
|
+
example: true
|
|
851
|
+
data:
|
|
852
|
+
type: object
|
|
853
|
+
properties:
|
|
854
|
+
success:
|
|
855
|
+
type: boolean
|
|
856
|
+
enum:
|
|
857
|
+
- true
|
|
858
|
+
example: true
|
|
859
|
+
'404':
|
|
860
|
+
$ref: '#/components/responses/NotFound'
|
|
861
|
+
'500':
|
|
862
|
+
$ref: '#/components/responses/InternalError'
|
|
863
|
+
/shares/{id}/visibility:
|
|
864
|
+
patch:
|
|
865
|
+
summary: Toggle share visibility
|
|
866
|
+
description: Show or hide a share from the dashboard
|
|
867
|
+
operationId: toggleShareVisibility
|
|
868
|
+
tags:
|
|
869
|
+
- Shares
|
|
870
|
+
parameters:
|
|
871
|
+
- name: id
|
|
872
|
+
in: path
|
|
873
|
+
required: true
|
|
874
|
+
schema:
|
|
875
|
+
type: integer
|
|
876
|
+
minimum: 1
|
|
877
|
+
requestBody:
|
|
878
|
+
required: true
|
|
879
|
+
content:
|
|
880
|
+
application/json:
|
|
881
|
+
schema:
|
|
882
|
+
type: object
|
|
883
|
+
properties:
|
|
884
|
+
is_hidden:
|
|
885
|
+
type: boolean
|
|
886
|
+
required:
|
|
887
|
+
- is_hidden
|
|
888
|
+
responses:
|
|
889
|
+
'200':
|
|
890
|
+
description: Visibility updated
|
|
891
|
+
content:
|
|
892
|
+
application/json:
|
|
893
|
+
schema:
|
|
894
|
+
type: object
|
|
895
|
+
properties:
|
|
896
|
+
success:
|
|
897
|
+
type: boolean
|
|
898
|
+
enum:
|
|
899
|
+
- true
|
|
900
|
+
example: true
|
|
901
|
+
data:
|
|
902
|
+
$ref: '#/components/schemas/Share'
|
|
903
|
+
'404':
|
|
904
|
+
$ref: '#/components/responses/NotFound'
|
|
905
|
+
'500':
|
|
906
|
+
$ref: '#/components/responses/InternalError'
|
|
907
|
+
/shares/{id}/pause:
|
|
908
|
+
patch:
|
|
909
|
+
summary: Toggle share paused status
|
|
910
|
+
description: Pause or unpause a share (owner-side)
|
|
911
|
+
operationId: toggleSharePaused
|
|
912
|
+
tags:
|
|
913
|
+
- Shares
|
|
914
|
+
parameters:
|
|
915
|
+
- name: id
|
|
916
|
+
in: path
|
|
917
|
+
required: true
|
|
918
|
+
schema:
|
|
919
|
+
type: integer
|
|
920
|
+
minimum: 1
|
|
921
|
+
requestBody:
|
|
922
|
+
required: true
|
|
923
|
+
content:
|
|
924
|
+
application/json:
|
|
925
|
+
schema:
|
|
926
|
+
type: object
|
|
927
|
+
properties:
|
|
928
|
+
is_paused:
|
|
929
|
+
type: boolean
|
|
930
|
+
required:
|
|
931
|
+
- is_paused
|
|
932
|
+
responses:
|
|
933
|
+
'200':
|
|
934
|
+
description: Paused status updated
|
|
935
|
+
content:
|
|
936
|
+
application/json:
|
|
937
|
+
schema:
|
|
938
|
+
type: object
|
|
939
|
+
properties:
|
|
940
|
+
success:
|
|
941
|
+
type: boolean
|
|
942
|
+
enum:
|
|
943
|
+
- true
|
|
944
|
+
example: true
|
|
945
|
+
data:
|
|
946
|
+
$ref: '#/components/schemas/Share'
|
|
947
|
+
'404':
|
|
948
|
+
$ref: '#/components/responses/NotFound'
|
|
949
|
+
'500':
|
|
950
|
+
$ref: '#/components/responses/InternalError'
|
|
951
|
+
/shares/{id}/transfer:
|
|
952
|
+
post:
|
|
953
|
+
summary: Transfer tag ownership
|
|
954
|
+
description: Transfer tag ownership to the shared-with user
|
|
955
|
+
operationId: transferShareOwnership
|
|
956
|
+
tags:
|
|
957
|
+
- Shares
|
|
958
|
+
parameters:
|
|
959
|
+
- name: id
|
|
960
|
+
in: path
|
|
961
|
+
required: true
|
|
962
|
+
schema:
|
|
963
|
+
type: integer
|
|
964
|
+
minimum: 1
|
|
965
|
+
responses:
|
|
966
|
+
'200':
|
|
967
|
+
description: Ownership transferred successfully
|
|
968
|
+
content:
|
|
969
|
+
application/json:
|
|
970
|
+
schema:
|
|
971
|
+
type: object
|
|
972
|
+
properties:
|
|
973
|
+
success:
|
|
974
|
+
type: boolean
|
|
975
|
+
enum:
|
|
976
|
+
- true
|
|
977
|
+
example: true
|
|
978
|
+
data:
|
|
979
|
+
type: object
|
|
980
|
+
properties:
|
|
981
|
+
success:
|
|
982
|
+
type: boolean
|
|
983
|
+
enum:
|
|
984
|
+
- true
|
|
985
|
+
example: true
|
|
986
|
+
'404':
|
|
987
|
+
$ref: '#/components/responses/NotFound'
|
|
988
|
+
'500':
|
|
989
|
+
$ref: '#/components/responses/InternalError'
|
|
990
|
+
/shares/bulk-delete:
|
|
991
|
+
post:
|
|
992
|
+
summary: Bulk delete shares
|
|
993
|
+
description: Delete multiple shares at once
|
|
994
|
+
operationId: bulkDeleteShares
|
|
995
|
+
tags:
|
|
996
|
+
- Shares
|
|
997
|
+
requestBody:
|
|
998
|
+
required: true
|
|
999
|
+
content:
|
|
1000
|
+
application/json:
|
|
1001
|
+
schema:
|
|
1002
|
+
type: object
|
|
1003
|
+
properties:
|
|
1004
|
+
ids:
|
|
1005
|
+
type: array
|
|
1006
|
+
items:
|
|
1007
|
+
type: integer
|
|
1008
|
+
description: Array of share IDs to delete
|
|
1009
|
+
example:
|
|
1010
|
+
- 1
|
|
1011
|
+
- 2
|
|
1012
|
+
- 3
|
|
1013
|
+
required:
|
|
1014
|
+
- ids
|
|
1015
|
+
responses:
|
|
1016
|
+
'200':
|
|
1017
|
+
description: Shares deleted successfully
|
|
1018
|
+
content:
|
|
1019
|
+
application/json:
|
|
1020
|
+
schema:
|
|
1021
|
+
type: object
|
|
1022
|
+
properties:
|
|
1023
|
+
success:
|
|
1024
|
+
type: boolean
|
|
1025
|
+
enum:
|
|
1026
|
+
- true
|
|
1027
|
+
example: true
|
|
1028
|
+
data:
|
|
1029
|
+
type: object
|
|
1030
|
+
properties:
|
|
1031
|
+
deleted:
|
|
1032
|
+
type: integer
|
|
1033
|
+
'400':
|
|
1034
|
+
$ref: '#/components/responses/ValidationError'
|
|
1035
|
+
'500':
|
|
1036
|
+
$ref: '#/components/responses/InternalError'
|
|
1037
|
+
/shares/accept:
|
|
1038
|
+
post:
|
|
1039
|
+
summary: Accept a pending share invitation
|
|
1040
|
+
description: >-
|
|
1041
|
+
Called from the /share/accept frontend page after the recipient clicks the link in their invitation email.
|
|
1042
|
+
Requires the authenticated user to be the invited recipient (`shared_with_user_id`). On success the share
|
|
1043
|
+
transitions from `pending` to `accepted` and data access is enabled.
|
|
1044
|
+
operationId: acceptShare
|
|
1045
|
+
tags:
|
|
1046
|
+
- Shares
|
|
1047
|
+
requestBody:
|
|
1048
|
+
required: true
|
|
1049
|
+
content:
|
|
1050
|
+
application/json:
|
|
1051
|
+
schema:
|
|
1052
|
+
type: object
|
|
1053
|
+
properties:
|
|
1054
|
+
token:
|
|
1055
|
+
type: string
|
|
1056
|
+
description: Opaque invitation token delivered via email. Hashed server-side before lookup.
|
|
1057
|
+
required:
|
|
1058
|
+
- token
|
|
1059
|
+
responses:
|
|
1060
|
+
'200':
|
|
1061
|
+
description: Share accepted
|
|
1062
|
+
content:
|
|
1063
|
+
application/json:
|
|
1064
|
+
schema:
|
|
1065
|
+
type: object
|
|
1066
|
+
properties:
|
|
1067
|
+
success:
|
|
1068
|
+
type: boolean
|
|
1069
|
+
enum:
|
|
1070
|
+
- true
|
|
1071
|
+
data:
|
|
1072
|
+
type: object
|
|
1073
|
+
properties:
|
|
1074
|
+
id:
|
|
1075
|
+
type: integer
|
|
1076
|
+
status:
|
|
1077
|
+
type: string
|
|
1078
|
+
enum:
|
|
1079
|
+
- accepted
|
|
1080
|
+
already:
|
|
1081
|
+
type: boolean
|
|
1082
|
+
description: True if the share was already accepted.
|
|
1083
|
+
'400':
|
|
1084
|
+
$ref: '#/components/responses/ValidationError'
|
|
1085
|
+
'404':
|
|
1086
|
+
$ref: '#/components/responses/NotFound'
|
|
1087
|
+
/shares/reject:
|
|
1088
|
+
post:
|
|
1089
|
+
summary: Decline a pending share invitation via emailed token
|
|
1090
|
+
description: >-
|
|
1091
|
+
Called from the /share/reject frontend page after the recipient clicks the decline link in their invitation
|
|
1092
|
+
email. Same token and auth model as /shares/accept (requires the authenticated user to be the invited
|
|
1093
|
+
recipient); on success the share transitions from `pending` to `rejected`. Cannot be used on already-accepted
|
|
1094
|
+
shares (delete instead, via DELETE /shares/{id}).
|
|
1095
|
+
operationId: rejectShareByToken
|
|
1096
|
+
tags:
|
|
1097
|
+
- Shares
|
|
1098
|
+
requestBody:
|
|
1099
|
+
required: true
|
|
1100
|
+
content:
|
|
1101
|
+
application/json:
|
|
1102
|
+
schema:
|
|
1103
|
+
type: object
|
|
1104
|
+
properties:
|
|
1105
|
+
token:
|
|
1106
|
+
type: string
|
|
1107
|
+
description: Opaque invitation token delivered via email. Hashed server-side before lookup.
|
|
1108
|
+
required:
|
|
1109
|
+
- token
|
|
1110
|
+
responses:
|
|
1111
|
+
'200':
|
|
1112
|
+
description: Share rejected
|
|
1113
|
+
content:
|
|
1114
|
+
application/json:
|
|
1115
|
+
schema:
|
|
1116
|
+
type: object
|
|
1117
|
+
properties:
|
|
1118
|
+
success:
|
|
1119
|
+
type: boolean
|
|
1120
|
+
enum:
|
|
1121
|
+
- true
|
|
1122
|
+
data:
|
|
1123
|
+
type: object
|
|
1124
|
+
properties:
|
|
1125
|
+
id:
|
|
1126
|
+
type: integer
|
|
1127
|
+
status:
|
|
1128
|
+
type: string
|
|
1129
|
+
enum:
|
|
1130
|
+
- rejected
|
|
1131
|
+
already:
|
|
1132
|
+
type: boolean
|
|
1133
|
+
description: True if the share was already rejected.
|
|
1134
|
+
'400':
|
|
1135
|
+
$ref: '#/components/responses/ValidationError'
|
|
1136
|
+
'404':
|
|
1137
|
+
$ref: '#/components/responses/NotFound'
|
|
1138
|
+
/shares/{id}/reject:
|
|
1139
|
+
post:
|
|
1140
|
+
summary: Reject a pending share invitation
|
|
1141
|
+
description: >-
|
|
1142
|
+
Called from the recipient's SharingManager UI. Marks the share as `rejected`; cannot be used on already-accepted
|
|
1143
|
+
shares (delete instead).
|
|
1144
|
+
operationId: rejectShare
|
|
1145
|
+
tags:
|
|
1146
|
+
- Shares
|
|
1147
|
+
parameters:
|
|
1148
|
+
- name: id
|
|
1149
|
+
in: path
|
|
1150
|
+
required: true
|
|
1151
|
+
schema:
|
|
1152
|
+
type: integer
|
|
1153
|
+
responses:
|
|
1154
|
+
'200':
|
|
1155
|
+
description: Share rejected
|
|
1156
|
+
content:
|
|
1157
|
+
application/json:
|
|
1158
|
+
schema:
|
|
1159
|
+
type: object
|
|
1160
|
+
properties:
|
|
1161
|
+
success:
|
|
1162
|
+
type: boolean
|
|
1163
|
+
enum:
|
|
1164
|
+
- true
|
|
1165
|
+
data:
|
|
1166
|
+
type: object
|
|
1167
|
+
properties:
|
|
1168
|
+
id:
|
|
1169
|
+
type: integer
|
|
1170
|
+
status:
|
|
1171
|
+
type: string
|
|
1172
|
+
enum:
|
|
1173
|
+
- rejected
|
|
1174
|
+
'400':
|
|
1175
|
+
$ref: '#/components/responses/ValidationError'
|
|
1176
|
+
'404':
|
|
1177
|
+
$ref: '#/components/responses/NotFound'
|
|
1178
|
+
/shared-graphs:
|
|
1179
|
+
get:
|
|
1180
|
+
summary: Get my shared graphs
|
|
1181
|
+
description: Retrieve shared graphs created by the authenticated user
|
|
1182
|
+
operationId: getMySharedGraphs
|
|
1183
|
+
tags:
|
|
1184
|
+
- Shared Graphs
|
|
1185
|
+
responses:
|
|
1186
|
+
'200':
|
|
1187
|
+
description: List of shared graphs
|
|
1188
|
+
content:
|
|
1189
|
+
application/json:
|
|
1190
|
+
schema:
|
|
1191
|
+
type: object
|
|
1192
|
+
properties:
|
|
1193
|
+
success:
|
|
1194
|
+
type: boolean
|
|
1195
|
+
enum:
|
|
1196
|
+
- true
|
|
1197
|
+
example: true
|
|
1198
|
+
data:
|
|
1199
|
+
type: array
|
|
1200
|
+
items:
|
|
1201
|
+
$ref: '#/components/schemas/SharedGraph'
|
|
1202
|
+
'500':
|
|
1203
|
+
$ref: '#/components/responses/InternalError'
|
|
1204
|
+
post:
|
|
1205
|
+
summary: Create a shared graph
|
|
1206
|
+
description: Create a publicly shareable graph snapshot for SNS sharing
|
|
1207
|
+
operationId: createSharedGraph
|
|
1208
|
+
tags:
|
|
1209
|
+
- Shared Graphs
|
|
1210
|
+
requestBody:
|
|
1211
|
+
required: true
|
|
1212
|
+
content:
|
|
1213
|
+
application/json:
|
|
1214
|
+
schema:
|
|
1215
|
+
$ref: '#/components/schemas/CreateSharedGraphInput'
|
|
1216
|
+
responses:
|
|
1217
|
+
'201':
|
|
1218
|
+
description: Shared graph created
|
|
1219
|
+
content:
|
|
1220
|
+
application/json:
|
|
1221
|
+
schema:
|
|
1222
|
+
type: object
|
|
1223
|
+
properties:
|
|
1224
|
+
success:
|
|
1225
|
+
type: boolean
|
|
1226
|
+
enum:
|
|
1227
|
+
- true
|
|
1228
|
+
example: true
|
|
1229
|
+
data:
|
|
1230
|
+
$ref: '#/components/schemas/SharedGraph'
|
|
1231
|
+
'400':
|
|
1232
|
+
$ref: '#/components/responses/ValidationError'
|
|
1233
|
+
'500':
|
|
1234
|
+
$ref: '#/components/responses/InternalError'
|
|
1235
|
+
/shared-graphs/{id}:
|
|
1236
|
+
put:
|
|
1237
|
+
summary: Update a shared graph
|
|
1238
|
+
description: Update title or description of a shared graph
|
|
1239
|
+
operationId: updateSharedGraph
|
|
1240
|
+
tags:
|
|
1241
|
+
- Shared Graphs
|
|
1242
|
+
parameters:
|
|
1243
|
+
- name: id
|
|
1244
|
+
in: path
|
|
1245
|
+
required: true
|
|
1246
|
+
schema:
|
|
1247
|
+
type: string
|
|
1248
|
+
requestBody:
|
|
1249
|
+
required: true
|
|
1250
|
+
content:
|
|
1251
|
+
application/json:
|
|
1252
|
+
schema:
|
|
1253
|
+
type: object
|
|
1254
|
+
properties:
|
|
1255
|
+
title:
|
|
1256
|
+
type: string
|
|
1257
|
+
description:
|
|
1258
|
+
type: string
|
|
1259
|
+
responses:
|
|
1260
|
+
'200':
|
|
1261
|
+
description: Shared graph updated
|
|
1262
|
+
content:
|
|
1263
|
+
application/json:
|
|
1264
|
+
schema:
|
|
1265
|
+
type: object
|
|
1266
|
+
properties:
|
|
1267
|
+
success:
|
|
1268
|
+
type: boolean
|
|
1269
|
+
enum:
|
|
1270
|
+
- true
|
|
1271
|
+
example: true
|
|
1272
|
+
data:
|
|
1273
|
+
$ref: '#/components/schemas/SharedGraph'
|
|
1274
|
+
'404':
|
|
1275
|
+
$ref: '#/components/responses/NotFound'
|
|
1276
|
+
'500':
|
|
1277
|
+
$ref: '#/components/responses/InternalError'
|
|
1278
|
+
delete:
|
|
1279
|
+
summary: Delete a shared graph
|
|
1280
|
+
description: Delete a shared graph by ID
|
|
1281
|
+
operationId: deleteSharedGraph
|
|
1282
|
+
tags:
|
|
1283
|
+
- Shared Graphs
|
|
1284
|
+
parameters:
|
|
1285
|
+
- name: id
|
|
1286
|
+
in: path
|
|
1287
|
+
required: true
|
|
1288
|
+
schema:
|
|
1289
|
+
type: string
|
|
1290
|
+
responses:
|
|
1291
|
+
'200':
|
|
1292
|
+
description: Shared graph deleted
|
|
1293
|
+
content:
|
|
1294
|
+
application/json:
|
|
1295
|
+
schema:
|
|
1296
|
+
type: object
|
|
1297
|
+
properties:
|
|
1298
|
+
success:
|
|
1299
|
+
type: boolean
|
|
1300
|
+
enum:
|
|
1301
|
+
- true
|
|
1302
|
+
example: true
|
|
1303
|
+
data:
|
|
1304
|
+
type: object
|
|
1305
|
+
properties:
|
|
1306
|
+
success:
|
|
1307
|
+
type: boolean
|
|
1308
|
+
enum:
|
|
1309
|
+
- true
|
|
1310
|
+
example: true
|
|
1311
|
+
'404':
|
|
1312
|
+
$ref: '#/components/responses/NotFound'
|
|
1313
|
+
'500':
|
|
1314
|
+
$ref: '#/components/responses/InternalError'
|
|
1315
|
+
/shared-graphs/{id}/toggle:
|
|
1316
|
+
patch:
|
|
1317
|
+
summary: Toggle shared graph active status
|
|
1318
|
+
description: Activate or deactivate a shared graph
|
|
1319
|
+
operationId: toggleSharedGraphActive
|
|
1320
|
+
tags:
|
|
1321
|
+
- Shared Graphs
|
|
1322
|
+
parameters:
|
|
1323
|
+
- name: id
|
|
1324
|
+
in: path
|
|
1325
|
+
required: true
|
|
1326
|
+
schema:
|
|
1327
|
+
type: string
|
|
1328
|
+
responses:
|
|
1329
|
+
'200':
|
|
1330
|
+
description: Status toggled
|
|
1331
|
+
content:
|
|
1332
|
+
application/json:
|
|
1333
|
+
schema:
|
|
1334
|
+
type: object
|
|
1335
|
+
properties:
|
|
1336
|
+
success:
|
|
1337
|
+
type: boolean
|
|
1338
|
+
enum:
|
|
1339
|
+
- true
|
|
1340
|
+
example: true
|
|
1341
|
+
data:
|
|
1342
|
+
type: object
|
|
1343
|
+
properties:
|
|
1344
|
+
is_active:
|
|
1345
|
+
type: integer
|
|
1346
|
+
description: 1 for active, 0 for inactive
|
|
1347
|
+
'404':
|
|
1348
|
+
$ref: '#/components/responses/NotFound'
|
|
1349
|
+
'500':
|
|
1350
|
+
$ref: '#/components/responses/InternalError'
|
|
1351
|
+
/shared-graphs/bulk-delete:
|
|
1352
|
+
post:
|
|
1353
|
+
summary: Bulk delete shared graphs
|
|
1354
|
+
description: Delete multiple shared graphs at once
|
|
1355
|
+
operationId: bulkDeleteSharedGraphs
|
|
1356
|
+
tags:
|
|
1357
|
+
- Shared Graphs
|
|
1358
|
+
requestBody:
|
|
1359
|
+
required: true
|
|
1360
|
+
content:
|
|
1361
|
+
application/json:
|
|
1362
|
+
schema:
|
|
1363
|
+
type: object
|
|
1364
|
+
properties:
|
|
1365
|
+
ids:
|
|
1366
|
+
type: array
|
|
1367
|
+
items:
|
|
1368
|
+
type: string
|
|
1369
|
+
description: Array of shared graph IDs to delete
|
|
1370
|
+
example:
|
|
1371
|
+
- abc123
|
|
1372
|
+
- def456
|
|
1373
|
+
required:
|
|
1374
|
+
- ids
|
|
1375
|
+
responses:
|
|
1376
|
+
'200':
|
|
1377
|
+
description: Shared graphs deleted
|
|
1378
|
+
content:
|
|
1379
|
+
application/json:
|
|
1380
|
+
schema:
|
|
1381
|
+
type: object
|
|
1382
|
+
properties:
|
|
1383
|
+
success:
|
|
1384
|
+
type: boolean
|
|
1385
|
+
enum:
|
|
1386
|
+
- true
|
|
1387
|
+
example: true
|
|
1388
|
+
data:
|
|
1389
|
+
type: object
|
|
1390
|
+
properties:
|
|
1391
|
+
deleted:
|
|
1392
|
+
type: integer
|
|
1393
|
+
'400':
|
|
1394
|
+
$ref: '#/components/responses/ValidationError'
|
|
1395
|
+
'500':
|
|
1396
|
+
$ref: '#/components/responses/InternalError'
|
|
1397
|
+
/public/shared-graphs/{id}:
|
|
1398
|
+
get:
|
|
1399
|
+
summary: Get public shared graph
|
|
1400
|
+
description: Retrieve a shared graph's public data (no authentication required)
|
|
1401
|
+
operationId: getPublicSharedGraph
|
|
1402
|
+
tags:
|
|
1403
|
+
- Shared Graphs
|
|
1404
|
+
security: []
|
|
1405
|
+
parameters:
|
|
1406
|
+
- name: id
|
|
1407
|
+
in: path
|
|
1408
|
+
required: true
|
|
1409
|
+
schema:
|
|
1410
|
+
type: string
|
|
1411
|
+
responses:
|
|
1412
|
+
'200':
|
|
1413
|
+
description: Shared graph public data
|
|
1414
|
+
content:
|
|
1415
|
+
application/json:
|
|
1416
|
+
schema:
|
|
1417
|
+
type: object
|
|
1418
|
+
properties:
|
|
1419
|
+
success:
|
|
1420
|
+
type: boolean
|
|
1421
|
+
enum:
|
|
1422
|
+
- true
|
|
1423
|
+
example: true
|
|
1424
|
+
data:
|
|
1425
|
+
$ref: '#/components/schemas/SharedGraphPublic'
|
|
1426
|
+
'404':
|
|
1427
|
+
$ref: '#/components/responses/NotFound'
|
|
1428
|
+
/public/shared-graphs/{id}/og-image:
|
|
1429
|
+
get:
|
|
1430
|
+
summary: Generate OG image for shared graph
|
|
1431
|
+
description: Generate an Open Graph image for a shared graph (no authentication required)
|
|
1432
|
+
operationId: getSharedGraphOgImage
|
|
1433
|
+
tags:
|
|
1434
|
+
- Shared Graphs
|
|
1435
|
+
security: []
|
|
1436
|
+
parameters:
|
|
1437
|
+
- name: id
|
|
1438
|
+
in: path
|
|
1439
|
+
required: true
|
|
1440
|
+
schema:
|
|
1441
|
+
type: string
|
|
1442
|
+
responses:
|
|
1443
|
+
'200':
|
|
1444
|
+
description: OG image
|
|
1445
|
+
content:
|
|
1446
|
+
image/png:
|
|
1447
|
+
schema:
|
|
1448
|
+
type: string
|
|
1449
|
+
format: binary
|
|
1450
|
+
'404':
|
|
1451
|
+
$ref: '#/components/responses/NotFound'
|
|
1452
|
+
/records/export:
|
|
1453
|
+
get:
|
|
1454
|
+
summary: Export records as CSV
|
|
1455
|
+
description: Export records to CSV format with optional filtering
|
|
1456
|
+
operationId: exportRecords
|
|
1457
|
+
tags:
|
|
1458
|
+
- Records
|
|
1459
|
+
parameters:
|
|
1460
|
+
- name: name
|
|
1461
|
+
in: query
|
|
1462
|
+
description: Filter by record name (supports wildcards with *)
|
|
1463
|
+
schema:
|
|
1464
|
+
type: string
|
|
1465
|
+
- name: start_date
|
|
1466
|
+
in: query
|
|
1467
|
+
description: Filter by start date (ISO 8601)
|
|
1468
|
+
schema:
|
|
1469
|
+
type: string
|
|
1470
|
+
format: date-time
|
|
1471
|
+
- name: end_date
|
|
1472
|
+
in: query
|
|
1473
|
+
description: Filter by end date (ISO 8601)
|
|
1474
|
+
schema:
|
|
1475
|
+
type: string
|
|
1476
|
+
format: date-time
|
|
1477
|
+
responses:
|
|
1478
|
+
'200':
|
|
1479
|
+
description: CSV file containing records
|
|
1480
|
+
content:
|
|
1481
|
+
text/csv:
|
|
1482
|
+
schema:
|
|
1483
|
+
type: string
|
|
1484
|
+
format: binary
|
|
1485
|
+
example: |-
|
|
1486
|
+
Tag,Value,Unit,Recorded At
|
|
1487
|
+
"temperature",25.5,"°C","2024-01-01T10:00:00Z"
|
|
1488
|
+
'500':
|
|
1489
|
+
$ref: '#/components/responses/InternalError'
|
|
1490
|
+
/records/import:
|
|
1491
|
+
post:
|
|
1492
|
+
summary: Import records from CSV
|
|
1493
|
+
description: Import records from CSV file. Records with matching tag and recorded_at will be updated, others will be created.
|
|
1494
|
+
operationId: importRecords
|
|
1495
|
+
tags:
|
|
1496
|
+
- Records
|
|
1497
|
+
requestBody:
|
|
1498
|
+
required: true
|
|
1499
|
+
content:
|
|
1500
|
+
multipart/form-data:
|
|
1501
|
+
schema:
|
|
1502
|
+
type: object
|
|
1503
|
+
properties:
|
|
1504
|
+
file:
|
|
1505
|
+
type: string
|
|
1506
|
+
format: binary
|
|
1507
|
+
description: 'CSV file to import (columns: Tag, Value, Unit, Recorded At)'
|
|
1508
|
+
required:
|
|
1509
|
+
- file
|
|
1510
|
+
responses:
|
|
1511
|
+
'200':
|
|
1512
|
+
description: Import completed successfully
|
|
1513
|
+
content:
|
|
1514
|
+
application/json:
|
|
1515
|
+
schema:
|
|
1516
|
+
$ref: '#/components/schemas/ImportResponse'
|
|
1517
|
+
'400':
|
|
1518
|
+
$ref: '#/components/responses/ValidationError'
|
|
1519
|
+
'500':
|
|
1520
|
+
$ref: '#/components/responses/InternalError'
|
|
1521
|
+
/records/stats:
|
|
1522
|
+
get:
|
|
1523
|
+
summary: Get record statistics
|
|
1524
|
+
description: Get aggregated statistics for records with a specific name
|
|
1525
|
+
operationId: getRecordStats
|
|
1526
|
+
tags:
|
|
1527
|
+
- Statistics
|
|
1528
|
+
parameters:
|
|
1529
|
+
- name: name
|
|
1530
|
+
in: query
|
|
1531
|
+
required: true
|
|
1532
|
+
description: Record name to get statistics for
|
|
1533
|
+
schema:
|
|
1534
|
+
type: string
|
|
1535
|
+
responses:
|
|
1536
|
+
'200':
|
|
1537
|
+
description: Statistics for the specified record name
|
|
1538
|
+
content:
|
|
1539
|
+
application/json:
|
|
1540
|
+
schema:
|
|
1541
|
+
$ref: '#/components/schemas/StatsResponse'
|
|
1542
|
+
'400':
|
|
1543
|
+
$ref: '#/components/responses/ValidationError'
|
|
1544
|
+
'404':
|
|
1545
|
+
$ref: '#/components/responses/NotFound'
|
|
1546
|
+
'500':
|
|
1547
|
+
$ref: '#/components/responses/InternalError'
|
|
1548
|
+
/media/upload:
|
|
1549
|
+
post:
|
|
1550
|
+
summary: Upload media file
|
|
1551
|
+
description: Upload a media file (image, video, or audio). Maximum file size is 100MB.
|
|
1552
|
+
operationId: uploadMedia
|
|
1553
|
+
tags:
|
|
1554
|
+
- Media
|
|
1555
|
+
requestBody:
|
|
1556
|
+
required: true
|
|
1557
|
+
content:
|
|
1558
|
+
multipart/form-data:
|
|
1559
|
+
schema:
|
|
1560
|
+
type: object
|
|
1561
|
+
properties:
|
|
1562
|
+
file:
|
|
1563
|
+
type: string
|
|
1564
|
+
format: binary
|
|
1565
|
+
description: Media file to upload (max 100MB)
|
|
1566
|
+
required:
|
|
1567
|
+
- file
|
|
1568
|
+
responses:
|
|
1569
|
+
'200':
|
|
1570
|
+
description: File uploaded successfully
|
|
1571
|
+
content:
|
|
1572
|
+
application/json:
|
|
1573
|
+
schema:
|
|
1574
|
+
type: object
|
|
1575
|
+
properties:
|
|
1576
|
+
success:
|
|
1577
|
+
type: boolean
|
|
1578
|
+
enum:
|
|
1579
|
+
- true
|
|
1580
|
+
example: true
|
|
1581
|
+
data:
|
|
1582
|
+
type: object
|
|
1583
|
+
properties:
|
|
1584
|
+
url:
|
|
1585
|
+
type: string
|
|
1586
|
+
description: URL of the uploaded file
|
|
1587
|
+
key:
|
|
1588
|
+
type: string
|
|
1589
|
+
description: Unique key for the uploaded file
|
|
1590
|
+
mediaType:
|
|
1591
|
+
type: string
|
|
1592
|
+
enum:
|
|
1593
|
+
- image
|
|
1594
|
+
- video
|
|
1595
|
+
- audio
|
|
1596
|
+
description: Type of media
|
|
1597
|
+
filename:
|
|
1598
|
+
type: string
|
|
1599
|
+
description: Original filename
|
|
1600
|
+
size:
|
|
1601
|
+
type: integer
|
|
1602
|
+
description: File size in bytes
|
|
1603
|
+
contentType:
|
|
1604
|
+
type: string
|
|
1605
|
+
description: MIME content type
|
|
1606
|
+
'400':
|
|
1607
|
+
$ref: '#/components/responses/ValidationError'
|
|
1608
|
+
'500':
|
|
1609
|
+
$ref: '#/components/responses/InternalError'
|
|
1610
|
+
/media/upload-url:
|
|
1611
|
+
post:
|
|
1612
|
+
summary: Get upload URL info
|
|
1613
|
+
description: Get upload URL information for a media file
|
|
1614
|
+
operationId: getMediaUploadUrl
|
|
1615
|
+
tags:
|
|
1616
|
+
- Media
|
|
1617
|
+
requestBody:
|
|
1618
|
+
required: true
|
|
1619
|
+
content:
|
|
1620
|
+
application/json:
|
|
1621
|
+
schema:
|
|
1622
|
+
type: object
|
|
1623
|
+
properties:
|
|
1624
|
+
filename:
|
|
1625
|
+
type: string
|
|
1626
|
+
description: Original filename
|
|
1627
|
+
contentType:
|
|
1628
|
+
type: string
|
|
1629
|
+
description: MIME content type
|
|
1630
|
+
size:
|
|
1631
|
+
type: integer
|
|
1632
|
+
description: File size in bytes
|
|
1633
|
+
required:
|
|
1634
|
+
- filename
|
|
1635
|
+
- contentType
|
|
1636
|
+
- size
|
|
1637
|
+
responses:
|
|
1638
|
+
'200':
|
|
1639
|
+
description: Upload URL info
|
|
1640
|
+
content:
|
|
1641
|
+
application/json:
|
|
1642
|
+
schema:
|
|
1643
|
+
type: object
|
|
1644
|
+
properties:
|
|
1645
|
+
success:
|
|
1646
|
+
type: boolean
|
|
1647
|
+
enum:
|
|
1648
|
+
- true
|
|
1649
|
+
example: true
|
|
1650
|
+
data:
|
|
1651
|
+
type: object
|
|
1652
|
+
properties:
|
|
1653
|
+
key:
|
|
1654
|
+
type: string
|
|
1655
|
+
description: Unique key for the file
|
|
1656
|
+
mediaType:
|
|
1657
|
+
type: string
|
|
1658
|
+
description: Type of media
|
|
1659
|
+
uploadMethod:
|
|
1660
|
+
type: string
|
|
1661
|
+
example: direct
|
|
1662
|
+
description: Upload method
|
|
1663
|
+
'400':
|
|
1664
|
+
$ref: '#/components/responses/ValidationError'
|
|
1665
|
+
'500':
|
|
1666
|
+
$ref: '#/components/responses/InternalError'
|
|
1667
|
+
/media/signed-url/{key}:
|
|
1668
|
+
get:
|
|
1669
|
+
summary: Get signed URL for media access
|
|
1670
|
+
description: Get a signed URL to access a media file
|
|
1671
|
+
operationId: getMediaSignedUrl
|
|
1672
|
+
tags:
|
|
1673
|
+
- Media
|
|
1674
|
+
parameters:
|
|
1675
|
+
- name: key
|
|
1676
|
+
in: path
|
|
1677
|
+
required: true
|
|
1678
|
+
description: Media file key
|
|
1679
|
+
schema:
|
|
1680
|
+
type: string
|
|
1681
|
+
responses:
|
|
1682
|
+
'200':
|
|
1683
|
+
description: Signed URL
|
|
1684
|
+
content:
|
|
1685
|
+
application/json:
|
|
1686
|
+
schema:
|
|
1687
|
+
type: object
|
|
1688
|
+
properties:
|
|
1689
|
+
success:
|
|
1690
|
+
type: boolean
|
|
1691
|
+
enum:
|
|
1692
|
+
- true
|
|
1693
|
+
example: true
|
|
1694
|
+
data:
|
|
1695
|
+
type: object
|
|
1696
|
+
properties:
|
|
1697
|
+
signedUrl:
|
|
1698
|
+
type: string
|
|
1699
|
+
description: Signed URL for accessing the media file
|
|
1700
|
+
'404':
|
|
1701
|
+
$ref: '#/components/responses/NotFound'
|
|
1702
|
+
'500':
|
|
1703
|
+
$ref: '#/components/responses/InternalError'
|
|
1704
|
+
/media/delete/{key}:
|
|
1705
|
+
delete:
|
|
1706
|
+
summary: Delete media file
|
|
1707
|
+
description: Delete a media file by key
|
|
1708
|
+
operationId: deleteMedia
|
|
1709
|
+
tags:
|
|
1710
|
+
- Media
|
|
1711
|
+
parameters:
|
|
1712
|
+
- name: key
|
|
1713
|
+
in: path
|
|
1714
|
+
required: true
|
|
1715
|
+
description: Media file key
|
|
1716
|
+
schema:
|
|
1717
|
+
type: string
|
|
1718
|
+
responses:
|
|
1719
|
+
'200':
|
|
1720
|
+
description: Media file deleted
|
|
1721
|
+
content:
|
|
1722
|
+
application/json:
|
|
1723
|
+
schema:
|
|
1724
|
+
type: object
|
|
1725
|
+
properties:
|
|
1726
|
+
success:
|
|
1727
|
+
type: boolean
|
|
1728
|
+
enum:
|
|
1729
|
+
- true
|
|
1730
|
+
example: true
|
|
1731
|
+
data:
|
|
1732
|
+
type: object
|
|
1733
|
+
properties:
|
|
1734
|
+
success:
|
|
1735
|
+
type: boolean
|
|
1736
|
+
enum:
|
|
1737
|
+
- true
|
|
1738
|
+
example: true
|
|
1739
|
+
'404':
|
|
1740
|
+
$ref: '#/components/responses/NotFound'
|
|
1741
|
+
'500':
|
|
1742
|
+
$ref: '#/components/responses/InternalError'
|
|
1743
|
+
/goals:
|
|
1744
|
+
get:
|
|
1745
|
+
summary: List all goals
|
|
1746
|
+
description: Retrieve all goals for the authenticated user
|
|
1747
|
+
operationId: getGoals
|
|
1748
|
+
tags:
|
|
1749
|
+
- Goals
|
|
1750
|
+
responses:
|
|
1751
|
+
'200':
|
|
1752
|
+
description: List of goals
|
|
1753
|
+
content:
|
|
1754
|
+
application/json:
|
|
1755
|
+
schema:
|
|
1756
|
+
type: object
|
|
1757
|
+
properties:
|
|
1758
|
+
success:
|
|
1759
|
+
type: boolean
|
|
1760
|
+
enum:
|
|
1761
|
+
- true
|
|
1762
|
+
example: true
|
|
1763
|
+
data:
|
|
1764
|
+
type: object
|
|
1765
|
+
properties:
|
|
1766
|
+
data:
|
|
1767
|
+
type: array
|
|
1768
|
+
items:
|
|
1769
|
+
$ref: '#/components/schemas/Goal'
|
|
1770
|
+
total:
|
|
1771
|
+
type: integer
|
|
1772
|
+
'500':
|
|
1773
|
+
$ref: '#/components/responses/InternalError'
|
|
1774
|
+
post:
|
|
1775
|
+
summary: Create a new goal
|
|
1776
|
+
description: Create a new goal for the authenticated user
|
|
1777
|
+
operationId: createGoal
|
|
1778
|
+
tags:
|
|
1779
|
+
- Goals
|
|
1780
|
+
requestBody:
|
|
1781
|
+
required: true
|
|
1782
|
+
content:
|
|
1783
|
+
application/json:
|
|
1784
|
+
schema:
|
|
1785
|
+
$ref: '#/components/schemas/CreateGoalInput'
|
|
1786
|
+
responses:
|
|
1787
|
+
'201':
|
|
1788
|
+
description: Goal created successfully
|
|
1789
|
+
content:
|
|
1790
|
+
application/json:
|
|
1791
|
+
schema:
|
|
1792
|
+
type: object
|
|
1793
|
+
properties:
|
|
1794
|
+
success:
|
|
1795
|
+
type: boolean
|
|
1796
|
+
enum:
|
|
1797
|
+
- true
|
|
1798
|
+
example: true
|
|
1799
|
+
data:
|
|
1800
|
+
$ref: '#/components/schemas/Goal'
|
|
1801
|
+
'400':
|
|
1802
|
+
$ref: '#/components/responses/ValidationError'
|
|
1803
|
+
'500':
|
|
1804
|
+
$ref: '#/components/responses/InternalError'
|
|
1805
|
+
/goals/{id}:
|
|
1806
|
+
put:
|
|
1807
|
+
summary: Update a goal
|
|
1808
|
+
description: Update an existing goal by ID
|
|
1809
|
+
operationId: updateGoal
|
|
1810
|
+
tags:
|
|
1811
|
+
- Goals
|
|
1812
|
+
parameters:
|
|
1813
|
+
- name: id
|
|
1814
|
+
in: path
|
|
1815
|
+
required: true
|
|
1816
|
+
schema:
|
|
1817
|
+
type: integer
|
|
1818
|
+
minimum: 1
|
|
1819
|
+
requestBody:
|
|
1820
|
+
required: true
|
|
1821
|
+
content:
|
|
1822
|
+
application/json:
|
|
1823
|
+
schema:
|
|
1824
|
+
$ref: '#/components/schemas/UpdateGoalInput'
|
|
1825
|
+
responses:
|
|
1826
|
+
'200':
|
|
1827
|
+
description: Goal updated successfully
|
|
1828
|
+
content:
|
|
1829
|
+
application/json:
|
|
1830
|
+
schema:
|
|
1831
|
+
type: object
|
|
1832
|
+
properties:
|
|
1833
|
+
success:
|
|
1834
|
+
type: boolean
|
|
1835
|
+
enum:
|
|
1836
|
+
- true
|
|
1837
|
+
example: true
|
|
1838
|
+
data:
|
|
1839
|
+
$ref: '#/components/schemas/Goal'
|
|
1840
|
+
'400':
|
|
1841
|
+
$ref: '#/components/responses/ValidationError'
|
|
1842
|
+
'404':
|
|
1843
|
+
$ref: '#/components/responses/NotFound'
|
|
1844
|
+
'500':
|
|
1845
|
+
$ref: '#/components/responses/InternalError'
|
|
1846
|
+
delete:
|
|
1847
|
+
summary: Delete a goal
|
|
1848
|
+
description: Delete a goal by ID
|
|
1849
|
+
operationId: deleteGoal
|
|
1850
|
+
tags:
|
|
1851
|
+
- Goals
|
|
1852
|
+
parameters:
|
|
1853
|
+
- name: id
|
|
1854
|
+
in: path
|
|
1855
|
+
required: true
|
|
1856
|
+
schema:
|
|
1857
|
+
type: integer
|
|
1858
|
+
minimum: 1
|
|
1859
|
+
responses:
|
|
1860
|
+
'200':
|
|
1861
|
+
description: Goal deleted successfully
|
|
1862
|
+
content:
|
|
1863
|
+
application/json:
|
|
1864
|
+
schema:
|
|
1865
|
+
type: object
|
|
1866
|
+
properties:
|
|
1867
|
+
success:
|
|
1868
|
+
type: boolean
|
|
1869
|
+
enum:
|
|
1870
|
+
- true
|
|
1871
|
+
example: true
|
|
1872
|
+
data:
|
|
1873
|
+
type: object
|
|
1874
|
+
properties:
|
|
1875
|
+
success:
|
|
1876
|
+
type: boolean
|
|
1877
|
+
enum:
|
|
1878
|
+
- true
|
|
1879
|
+
example: true
|
|
1880
|
+
'404':
|
|
1881
|
+
$ref: '#/components/responses/NotFound'
|
|
1882
|
+
'500':
|
|
1883
|
+
$ref: '#/components/responses/InternalError'
|
|
1884
|
+
/goals/bulk-delete:
|
|
1885
|
+
post:
|
|
1886
|
+
summary: Bulk delete goals
|
|
1887
|
+
description: Delete multiple goals at once by providing an array of IDs
|
|
1888
|
+
operationId: bulkDeleteGoals
|
|
1889
|
+
tags:
|
|
1890
|
+
- Goals
|
|
1891
|
+
requestBody:
|
|
1892
|
+
required: true
|
|
1893
|
+
content:
|
|
1894
|
+
application/json:
|
|
1895
|
+
schema:
|
|
1896
|
+
type: object
|
|
1897
|
+
properties:
|
|
1898
|
+
ids:
|
|
1899
|
+
type: array
|
|
1900
|
+
items:
|
|
1901
|
+
type: integer
|
|
1902
|
+
description: Array of goal IDs to delete
|
|
1903
|
+
example:
|
|
1904
|
+
- 1
|
|
1905
|
+
- 2
|
|
1906
|
+
- 3
|
|
1907
|
+
required:
|
|
1908
|
+
- ids
|
|
1909
|
+
responses:
|
|
1910
|
+
'200':
|
|
1911
|
+
description: Goals deleted successfully
|
|
1912
|
+
content:
|
|
1913
|
+
application/json:
|
|
1914
|
+
schema:
|
|
1915
|
+
type: object
|
|
1916
|
+
properties:
|
|
1917
|
+
success:
|
|
1918
|
+
type: boolean
|
|
1919
|
+
enum:
|
|
1920
|
+
- true
|
|
1921
|
+
example: true
|
|
1922
|
+
data:
|
|
1923
|
+
type: object
|
|
1924
|
+
properties:
|
|
1925
|
+
deleted:
|
|
1926
|
+
type: integer
|
|
1927
|
+
description: Number of goals deleted
|
|
1928
|
+
'400':
|
|
1929
|
+
$ref: '#/components/responses/ValidationError'
|
|
1930
|
+
'500':
|
|
1931
|
+
$ref: '#/components/responses/InternalError'
|
|
1932
|
+
/goals/progress:
|
|
1933
|
+
get:
|
|
1934
|
+
summary: Get goal progress
|
|
1935
|
+
description: >-
|
|
1936
|
+
Retrieve progress for all visible goals of the authenticated user. Computes current values from latest records,
|
|
1937
|
+
including virtual tags (e.g. BMI). Used by NASEBANAL Target for cross-app goal tracking.
|
|
1938
|
+
operationId: getGoalProgress
|
|
1939
|
+
tags:
|
|
1940
|
+
- Goals
|
|
1941
|
+
responses:
|
|
1942
|
+
'200':
|
|
1943
|
+
description: Goal progress list
|
|
1944
|
+
content:
|
|
1945
|
+
application/json:
|
|
1946
|
+
schema:
|
|
1947
|
+
type: object
|
|
1948
|
+
properties:
|
|
1949
|
+
success:
|
|
1950
|
+
type: boolean
|
|
1951
|
+
enum:
|
|
1952
|
+
- true
|
|
1953
|
+
example: true
|
|
1954
|
+
data:
|
|
1955
|
+
type: object
|
|
1956
|
+
properties:
|
|
1957
|
+
data:
|
|
1958
|
+
type: array
|
|
1959
|
+
items:
|
|
1960
|
+
$ref: '#/components/schemas/GoalProgress'
|
|
1961
|
+
'500':
|
|
1962
|
+
$ref: '#/components/responses/InternalError'
|
|
1963
|
+
/goals/{id}/visibility:
|
|
1964
|
+
patch:
|
|
1965
|
+
summary: Toggle goal visibility
|
|
1966
|
+
description: Show or hide a goal
|
|
1967
|
+
operationId: toggleGoalVisibility
|
|
1968
|
+
tags:
|
|
1969
|
+
- Goals
|
|
1970
|
+
parameters:
|
|
1971
|
+
- name: id
|
|
1972
|
+
in: path
|
|
1973
|
+
required: true
|
|
1974
|
+
schema:
|
|
1975
|
+
type: integer
|
|
1976
|
+
minimum: 1
|
|
1977
|
+
requestBody:
|
|
1978
|
+
required: true
|
|
1979
|
+
content:
|
|
1980
|
+
application/json:
|
|
1981
|
+
schema:
|
|
1982
|
+
type: object
|
|
1983
|
+
properties:
|
|
1984
|
+
hide:
|
|
1985
|
+
type: boolean
|
|
1986
|
+
required:
|
|
1987
|
+
- hide
|
|
1988
|
+
responses:
|
|
1989
|
+
'200':
|
|
1990
|
+
description: Visibility updated
|
|
1991
|
+
content:
|
|
1992
|
+
application/json:
|
|
1993
|
+
schema:
|
|
1994
|
+
type: object
|
|
1995
|
+
properties:
|
|
1996
|
+
success:
|
|
1997
|
+
type: boolean
|
|
1998
|
+
enum:
|
|
1999
|
+
- true
|
|
2000
|
+
example: true
|
|
2001
|
+
data:
|
|
2002
|
+
$ref: '#/components/schemas/Goal'
|
|
2003
|
+
'404':
|
|
2004
|
+
$ref: '#/components/responses/NotFound'
|
|
2005
|
+
'500':
|
|
2006
|
+
$ref: '#/components/responses/InternalError'
|
|
2007
|
+
components:
|
|
2008
|
+
securitySchemes:
|
|
2009
|
+
BearerAuth:
|
|
2010
|
+
type: http
|
|
2011
|
+
scheme: bearer
|
|
2012
|
+
description: Personal Access Token (nbpat_...)
|
|
2013
|
+
parameters:
|
|
2014
|
+
RecordId:
|
|
2015
|
+
name: id
|
|
2016
|
+
in: path
|
|
2017
|
+
required: true
|
|
2018
|
+
description: Record ID
|
|
2019
|
+
schema:
|
|
2020
|
+
type: integer
|
|
2021
|
+
minimum: 1
|
|
2022
|
+
schemas:
|
|
2023
|
+
GoalProgress:
|
|
2024
|
+
type: object
|
|
2025
|
+
properties:
|
|
2026
|
+
goal_id:
|
|
2027
|
+
type: integer
|
|
2028
|
+
description: Goal ID
|
|
2029
|
+
tag_name:
|
|
2030
|
+
type: string
|
|
2031
|
+
description: Tag name associated with the goal
|
|
2032
|
+
tag_unit:
|
|
2033
|
+
type: string
|
|
2034
|
+
nullable: true
|
|
2035
|
+
description: Unit of the tag (e.g. kg, km)
|
|
2036
|
+
tag_data_type:
|
|
2037
|
+
type: string
|
|
2038
|
+
description: Data type of the tag (number, time, rank)
|
|
2039
|
+
virtual_tag_key:
|
|
2040
|
+
type: string
|
|
2041
|
+
nullable: true
|
|
2042
|
+
description: Virtual tag key if applicable (e.g. BodyWeight/BMI~)
|
|
2043
|
+
goal_value:
|
|
2044
|
+
type: number
|
|
2045
|
+
nullable: true
|
|
2046
|
+
description: Goal value for the goal
|
|
2047
|
+
direction:
|
|
2048
|
+
type: string
|
|
2049
|
+
enum:
|
|
2050
|
+
- above
|
|
2051
|
+
- below
|
|
2052
|
+
description: Direction of the goal (above = higher is better, below = lower is better)
|
|
2053
|
+
current:
|
|
2054
|
+
type: number
|
|
2055
|
+
nullable: true
|
|
2056
|
+
description: Current value computed from latest record
|
|
2057
|
+
met:
|
|
2058
|
+
type: boolean
|
|
2059
|
+
description: Whether the goal is currently met
|
|
2060
|
+
pct:
|
|
2061
|
+
type: integer
|
|
2062
|
+
description: Progress percentage (0-100)
|
|
2063
|
+
Record:
|
|
2064
|
+
type: object
|
|
2065
|
+
properties:
|
|
2066
|
+
id:
|
|
2067
|
+
type: integer
|
|
2068
|
+
description: Unique record ID
|
|
2069
|
+
name:
|
|
2070
|
+
type: string
|
|
2071
|
+
description: Record name/identifier
|
|
2072
|
+
value:
|
|
2073
|
+
type: number
|
|
2074
|
+
description: Numeric value
|
|
2075
|
+
unit:
|
|
2076
|
+
type: string
|
|
2077
|
+
nullable: true
|
|
2078
|
+
description: >-
|
|
2079
|
+
Unit of measurement. For a `time`-typed tag, `hour`, `minute`, or
|
|
2080
|
+
`second`, controlling whether the value formats as H:MM:SS,
|
|
2081
|
+
MM:SS, or decimal seconds.
|
|
2082
|
+
recorded_at:
|
|
2083
|
+
type: string
|
|
2084
|
+
format: date-time
|
|
2085
|
+
description: Timestamp when the value was recorded
|
|
2086
|
+
created_at:
|
|
2087
|
+
type: string
|
|
2088
|
+
format: date-time
|
|
2089
|
+
description: Timestamp when the record was created
|
|
2090
|
+
updated_at:
|
|
2091
|
+
type: string
|
|
2092
|
+
format: date-time
|
|
2093
|
+
description: Timestamp when the record was last updated
|
|
2094
|
+
owner_email:
|
|
2095
|
+
type: string
|
|
2096
|
+
description: >-
|
|
2097
|
+
Email of the tag's owner. Set on the caller's own rows too (it then
|
|
2098
|
+
holds the caller's own address), so compare it with the caller's to
|
|
2099
|
+
tell shared-in data apart.
|
|
2100
|
+
owner_name:
|
|
2101
|
+
type: string
|
|
2102
|
+
description: >-
|
|
2103
|
+
Display name the owner set in their profile. Absent when they set
|
|
2104
|
+
none. Clients name the owner by this, falling back to a masked email.
|
|
2105
|
+
required:
|
|
2106
|
+
- id
|
|
2107
|
+
- name
|
|
2108
|
+
- value
|
|
2109
|
+
- recorded_at
|
|
2110
|
+
- created_at
|
|
2111
|
+
- updated_at
|
|
2112
|
+
CreateRecordInput:
|
|
2113
|
+
type: object
|
|
2114
|
+
properties:
|
|
2115
|
+
name:
|
|
2116
|
+
type: string
|
|
2117
|
+
description: Record name/identifier
|
|
2118
|
+
example: temperature
|
|
2119
|
+
value:
|
|
2120
|
+
type: number
|
|
2121
|
+
description: Numeric value
|
|
2122
|
+
example: 25.5
|
|
2123
|
+
unit:
|
|
2124
|
+
type: string
|
|
2125
|
+
description: >-
|
|
2126
|
+
Unit of measurement. For a `time`-typed tag, must be `hour`,
|
|
2127
|
+
`minute`, or `second`, controlling whether the value formats as
|
|
2128
|
+
H:MM:SS, MM:SS, or decimal seconds.
|
|
2129
|
+
example: °C
|
|
2130
|
+
recorded_at:
|
|
2131
|
+
type: string
|
|
2132
|
+
format: date-time
|
|
2133
|
+
description: Timestamp when the value was recorded (defaults to current time)
|
|
2134
|
+
required:
|
|
2135
|
+
- name
|
|
2136
|
+
- value
|
|
2137
|
+
UpdateRecordInput:
|
|
2138
|
+
type: object
|
|
2139
|
+
properties:
|
|
2140
|
+
name:
|
|
2141
|
+
type: string
|
|
2142
|
+
description: Record name/identifier
|
|
2143
|
+
value:
|
|
2144
|
+
type: number
|
|
2145
|
+
description: Numeric value
|
|
2146
|
+
unit:
|
|
2147
|
+
type: string
|
|
2148
|
+
description: >-
|
|
2149
|
+
Unit of measurement. For a `time`-typed tag, must be `hour`,
|
|
2150
|
+
`minute`, or `second`, controlling whether the value formats as
|
|
2151
|
+
H:MM:SS, MM:SS, or decimal seconds.
|
|
2152
|
+
recorded_at:
|
|
2153
|
+
type: string
|
|
2154
|
+
format: date-time
|
|
2155
|
+
description: Timestamp when the value was recorded
|
|
2156
|
+
RecordStats:
|
|
2157
|
+
type: object
|
|
2158
|
+
properties:
|
|
2159
|
+
tag_id:
|
|
2160
|
+
type: integer
|
|
2161
|
+
description: Tag ID
|
|
2162
|
+
tag_name:
|
|
2163
|
+
type: string
|
|
2164
|
+
description: Tag name
|
|
2165
|
+
count:
|
|
2166
|
+
type: integer
|
|
2167
|
+
description: Total number of records
|
|
2168
|
+
min:
|
|
2169
|
+
type: number
|
|
2170
|
+
description: Minimum value
|
|
2171
|
+
min_recorded_at:
|
|
2172
|
+
type: string
|
|
2173
|
+
format: date-time
|
|
2174
|
+
nullable: true
|
|
2175
|
+
description: Timestamp when the minimum value was recorded
|
|
2176
|
+
max:
|
|
2177
|
+
type: number
|
|
2178
|
+
description: Maximum value
|
|
2179
|
+
max_recorded_at:
|
|
2180
|
+
type: string
|
|
2181
|
+
format: date-time
|
|
2182
|
+
nullable: true
|
|
2183
|
+
description: Timestamp when the maximum value was recorded
|
|
2184
|
+
average:
|
|
2185
|
+
type: number
|
|
2186
|
+
description: Average value
|
|
2187
|
+
latest:
|
|
2188
|
+
description: Latest record
|
|
2189
|
+
oneOf:
|
|
2190
|
+
- $ref: '#/components/schemas/Record'
|
|
2191
|
+
- type: object
|
|
2192
|
+
nullable: true
|
|
2193
|
+
required:
|
|
2194
|
+
- tag_id
|
|
2195
|
+
- tag_name
|
|
2196
|
+
- count
|
|
2197
|
+
- min
|
|
2198
|
+
- min_recorded_at
|
|
2199
|
+
- max
|
|
2200
|
+
- max_recorded_at
|
|
2201
|
+
- average
|
|
2202
|
+
- latest
|
|
2203
|
+
RecordResponse:
|
|
2204
|
+
type: object
|
|
2205
|
+
properties:
|
|
2206
|
+
success:
|
|
2207
|
+
type: boolean
|
|
2208
|
+
enum:
|
|
2209
|
+
- true
|
|
2210
|
+
example: true
|
|
2211
|
+
data:
|
|
2212
|
+
$ref: '#/components/schemas/Record'
|
|
2213
|
+
RecordListResponse:
|
|
2214
|
+
type: object
|
|
2215
|
+
properties:
|
|
2216
|
+
success:
|
|
2217
|
+
type: boolean
|
|
2218
|
+
enum:
|
|
2219
|
+
- true
|
|
2220
|
+
example: true
|
|
2221
|
+
data:
|
|
2222
|
+
type: object
|
|
2223
|
+
properties:
|
|
2224
|
+
data:
|
|
2225
|
+
type: array
|
|
2226
|
+
items:
|
|
2227
|
+
$ref: '#/components/schemas/Record'
|
|
2228
|
+
total:
|
|
2229
|
+
type: integer
|
|
2230
|
+
description: Total number of records matching the filter
|
|
2231
|
+
limit:
|
|
2232
|
+
type: integer
|
|
2233
|
+
description: Maximum number of records returned
|
|
2234
|
+
offset:
|
|
2235
|
+
type: integer
|
|
2236
|
+
description: Number of records skipped
|
|
2237
|
+
StatsResponse:
|
|
2238
|
+
type: object
|
|
2239
|
+
properties:
|
|
2240
|
+
success:
|
|
2241
|
+
type: boolean
|
|
2242
|
+
enum:
|
|
2243
|
+
- true
|
|
2244
|
+
example: true
|
|
2245
|
+
data:
|
|
2246
|
+
$ref: '#/components/schemas/RecordStats'
|
|
2247
|
+
ImportResponse:
|
|
2248
|
+
type: object
|
|
2249
|
+
properties:
|
|
2250
|
+
success:
|
|
2251
|
+
type: boolean
|
|
2252
|
+
enum:
|
|
2253
|
+
- true
|
|
2254
|
+
example: true
|
|
2255
|
+
data:
|
|
2256
|
+
type: object
|
|
2257
|
+
properties:
|
|
2258
|
+
imported:
|
|
2259
|
+
type: integer
|
|
2260
|
+
description: Total number of records processed
|
|
2261
|
+
example: 10
|
|
2262
|
+
created:
|
|
2263
|
+
type: integer
|
|
2264
|
+
description: Number of new records created
|
|
2265
|
+
example: 7
|
|
2266
|
+
updated:
|
|
2267
|
+
type: integer
|
|
2268
|
+
description: Number of existing records updated
|
|
2269
|
+
example: 3
|
|
2270
|
+
required:
|
|
2271
|
+
- imported
|
|
2272
|
+
- created
|
|
2273
|
+
- updated
|
|
2274
|
+
Tag:
|
|
2275
|
+
type: object
|
|
2276
|
+
properties:
|
|
2277
|
+
id:
|
|
2278
|
+
type: integer
|
|
2279
|
+
user_id:
|
|
2280
|
+
type: integer
|
|
2281
|
+
name:
|
|
2282
|
+
type: string
|
|
2283
|
+
unit:
|
|
2284
|
+
type: string
|
|
2285
|
+
nullable: true
|
|
2286
|
+
description: >-
|
|
2287
|
+
Unit of measurement. For a `time`-typed tag, must be `hour`,
|
|
2288
|
+
`minute`, or `second`, controlling whether the value formats as
|
|
2289
|
+
H:MM:SS, MM:SS, or decimal seconds.
|
|
2290
|
+
data_type:
|
|
2291
|
+
type: string
|
|
2292
|
+
enum:
|
|
2293
|
+
- binary
|
|
2294
|
+
- decimal
|
|
2295
|
+
- integer
|
|
2296
|
+
- time
|
|
2297
|
+
chart_type:
|
|
2298
|
+
type: string
|
|
2299
|
+
enum:
|
|
2300
|
+
- line
|
|
2301
|
+
- smooth
|
|
2302
|
+
- step
|
|
2303
|
+
is_hidden:
|
|
2304
|
+
type: integer
|
|
2305
|
+
description: 0 = visible, 1 = hidden
|
|
2306
|
+
data_type_options:
|
|
2307
|
+
type: array
|
|
2308
|
+
description: >-
|
|
2309
|
+
Which `data_type` this tag may be changed to, and why not for the
|
|
2310
|
+
rest. A change is safe exactly when no stored value changes meaning,
|
|
2311
|
+
which depends on the direction and on what is already stored — so
|
|
2312
|
+
this is server-computed rather than re-derived per client, and it
|
|
2313
|
+
carries a reason per option because the reason differs per option.
|
|
2314
|
+
A tag shared in from another user lists only its current type, not
|
|
2315
|
+
selectable: the recipient does not own the master data.
|
|
2316
|
+
items:
|
|
2317
|
+
type: object
|
|
2318
|
+
properties:
|
|
2319
|
+
value:
|
|
2320
|
+
type: string
|
|
2321
|
+
enum:
|
|
2322
|
+
- binary
|
|
2323
|
+
- decimal
|
|
2324
|
+
- integer
|
|
2325
|
+
- time
|
|
2326
|
+
selectable:
|
|
2327
|
+
type: boolean
|
|
2328
|
+
reason:
|
|
2329
|
+
type: string
|
|
2330
|
+
description: >-
|
|
2331
|
+
Why this type is unavailable; absent when selectable.
|
|
2332
|
+
`SCALE_CHANGE` — into or out of `time`, whose stored minutes
|
|
2333
|
+
would be re-read rather than converted (90 becomes 1:30).
|
|
2334
|
+
`NON_INTEGER_VALUES` — a stored value is not a whole number,
|
|
2335
|
+
so it would display rounded while the stored value stays
|
|
2336
|
+
fractional. `NON_BINARY_VALUES` — a stored value is neither 0
|
|
2337
|
+
nor 1.
|
|
2338
|
+
enum:
|
|
2339
|
+
- SCALE_CHANGE
|
|
2340
|
+
- NON_INTEGER_VALUES
|
|
2341
|
+
- NON_BINARY_VALUES
|
|
2342
|
+
required:
|
|
2343
|
+
- value
|
|
2344
|
+
- selectable
|
|
2345
|
+
created_at:
|
|
2346
|
+
type: string
|
|
2347
|
+
format: date-time
|
|
2348
|
+
updated_at:
|
|
2349
|
+
type: string
|
|
2350
|
+
format: date-time
|
|
2351
|
+
owner_email:
|
|
2352
|
+
type: string
|
|
2353
|
+
description: >-
|
|
2354
|
+
Email of the tag's owner. Set on the caller's own tags too (it then
|
|
2355
|
+
holds the caller's own address), so compare it with the caller's to
|
|
2356
|
+
tell shared-in tags apart.
|
|
2357
|
+
owner_name:
|
|
2358
|
+
type: string
|
|
2359
|
+
description: >-
|
|
2360
|
+
Display name the owner set in their profile. Absent when they set
|
|
2361
|
+
none. Clients name the owner by this, falling back to a masked email.
|
|
2362
|
+
required:
|
|
2363
|
+
- id
|
|
2364
|
+
- user_id
|
|
2365
|
+
- name
|
|
2366
|
+
- data_type
|
|
2367
|
+
- chart_type
|
|
2368
|
+
- created_at
|
|
2369
|
+
- updated_at
|
|
2370
|
+
Share:
|
|
2371
|
+
type: object
|
|
2372
|
+
properties:
|
|
2373
|
+
id:
|
|
2374
|
+
type: integer
|
|
2375
|
+
owner_user_id:
|
|
2376
|
+
type: integer
|
|
2377
|
+
shared_with_user_id:
|
|
2378
|
+
type: integer
|
|
2379
|
+
tag_id:
|
|
2380
|
+
type: integer
|
|
2381
|
+
nullable: true
|
|
2382
|
+
permission:
|
|
2383
|
+
type: string
|
|
2384
|
+
enum:
|
|
2385
|
+
- read
|
|
2386
|
+
- write
|
|
2387
|
+
is_hidden:
|
|
2388
|
+
type: integer
|
|
2389
|
+
status:
|
|
2390
|
+
type: string
|
|
2391
|
+
enum:
|
|
2392
|
+
- pending
|
|
2393
|
+
- accepted
|
|
2394
|
+
- rejected
|
|
2395
|
+
description: >-
|
|
2396
|
+
Approval status. New shares are created in 'pending' and require the recipient to click the accept link
|
|
2397
|
+
delivered via email. Only 'accepted' shares grant data access.
|
|
2398
|
+
expires_at:
|
|
2399
|
+
type: string
|
|
2400
|
+
format: date-time
|
|
2401
|
+
nullable: true
|
|
2402
|
+
description: Expiration timestamp for a pending invitation. After this time the invitation can no longer be accepted.
|
|
2403
|
+
created_at:
|
|
2404
|
+
type: string
|
|
2405
|
+
format: date-time
|
|
2406
|
+
updated_at:
|
|
2407
|
+
type: string
|
|
2408
|
+
format: date-time
|
|
2409
|
+
owner_email:
|
|
2410
|
+
type: string
|
|
2411
|
+
description: Email of the sharing owner. Set on shares addressed to the caller.
|
|
2412
|
+
owner_name:
|
|
2413
|
+
type: string
|
|
2414
|
+
description: >-
|
|
2415
|
+
Display name the sharing owner set in their profile. Absent when
|
|
2416
|
+
they set none.
|
|
2417
|
+
shared_with_email:
|
|
2418
|
+
type: string
|
|
2419
|
+
description: Email of the recipient. Set on shares the caller owns.
|
|
2420
|
+
shared_with_name:
|
|
2421
|
+
type: string
|
|
2422
|
+
description: >-
|
|
2423
|
+
Display name the recipient set in their profile. Set on shares the
|
|
2424
|
+
caller owns, and only once the share is `accepted`, so an invitation
|
|
2425
|
+
does not reveal the profile of someone who has not accepted it.
|
|
2426
|
+
required:
|
|
2427
|
+
- id
|
|
2428
|
+
- owner_user_id
|
|
2429
|
+
- shared_with_user_id
|
|
2430
|
+
- permission
|
|
2431
|
+
- status
|
|
2432
|
+
- created_at
|
|
2433
|
+
- updated_at
|
|
2434
|
+
SharedGraph:
|
|
2435
|
+
type: object
|
|
2436
|
+
properties:
|
|
2437
|
+
id:
|
|
2438
|
+
type: string
|
|
2439
|
+
tag_id:
|
|
2440
|
+
type: integer
|
|
2441
|
+
description: Primary tag ID (first tag). For legacy compatibility.
|
|
2442
|
+
tag_ids:
|
|
2443
|
+
type: array
|
|
2444
|
+
items:
|
|
2445
|
+
type: integer
|
|
2446
|
+
description: All tag IDs included in this shared graph.
|
|
2447
|
+
tag_names:
|
|
2448
|
+
type: array
|
|
2449
|
+
items:
|
|
2450
|
+
type: string
|
|
2451
|
+
description: All tag names included in this shared graph.
|
|
2452
|
+
title:
|
|
2453
|
+
type: string
|
|
2454
|
+
description:
|
|
2455
|
+
type: string
|
|
2456
|
+
nullable: true
|
|
2457
|
+
is_active:
|
|
2458
|
+
type: integer
|
|
2459
|
+
description: 1 = active, 0 = inactive
|
|
2460
|
+
view_count:
|
|
2461
|
+
type: integer
|
|
2462
|
+
created_at:
|
|
2463
|
+
type: string
|
|
2464
|
+
format: date-time
|
|
2465
|
+
required:
|
|
2466
|
+
- id
|
|
2467
|
+
- tag_id
|
|
2468
|
+
- tag_ids
|
|
2469
|
+
- tag_names
|
|
2470
|
+
- title
|
|
2471
|
+
- is_active
|
|
2472
|
+
- view_count
|
|
2473
|
+
- created_at
|
|
2474
|
+
SharedGraphPublic:
|
|
2475
|
+
type: object
|
|
2476
|
+
properties:
|
|
2477
|
+
id:
|
|
2478
|
+
type: string
|
|
2479
|
+
title:
|
|
2480
|
+
type: string
|
|
2481
|
+
nullable: true
|
|
2482
|
+
description:
|
|
2483
|
+
type: string
|
|
2484
|
+
nullable: true
|
|
2485
|
+
chart_data:
|
|
2486
|
+
type: string
|
|
2487
|
+
created_at:
|
|
2488
|
+
type: string
|
|
2489
|
+
format: date-time
|
|
2490
|
+
required:
|
|
2491
|
+
- id
|
|
2492
|
+
- chart_data
|
|
2493
|
+
- created_at
|
|
2494
|
+
CreateSharedGraphInput:
|
|
2495
|
+
type: object
|
|
2496
|
+
properties:
|
|
2497
|
+
tag_ids:
|
|
2498
|
+
type: array
|
|
2499
|
+
items:
|
|
2500
|
+
type: integer
|
|
2501
|
+
description: Tag IDs to include in the graph. Multiple tags can be specified.
|
|
2502
|
+
tag_id:
|
|
2503
|
+
type: integer
|
|
2504
|
+
description: Single tag ID (legacy, deprecated). Use tag_ids instead.
|
|
2505
|
+
title:
|
|
2506
|
+
type: string
|
|
2507
|
+
description: Title of the shared graph.
|
|
2508
|
+
description:
|
|
2509
|
+
type: string
|
|
2510
|
+
nullable: true
|
|
2511
|
+
start_date:
|
|
2512
|
+
type: string
|
|
2513
|
+
format: date
|
|
2514
|
+
description: Start date filter (YYYY-MM-DD). Defaults to 30 days ago if omitted.
|
|
2515
|
+
end_date:
|
|
2516
|
+
type: string
|
|
2517
|
+
format: date
|
|
2518
|
+
description: End date filter (YYYY-MM-DD).
|
|
2519
|
+
include_comments:
|
|
2520
|
+
type: boolean
|
|
2521
|
+
default: false
|
|
2522
|
+
description: Whether to include record comments in the snapshot.
|
|
2523
|
+
include_media:
|
|
2524
|
+
type: boolean
|
|
2525
|
+
default: false
|
|
2526
|
+
description: Whether to include media links (signed URLs) in the snapshot.
|
|
2527
|
+
auto_scale:
|
|
2528
|
+
type: boolean
|
|
2529
|
+
default: false
|
|
2530
|
+
description: Y-axis scale mode. true = auto scale to data range, false = start from 0.
|
|
2531
|
+
show_goals:
|
|
2532
|
+
type: boolean
|
|
2533
|
+
default: true
|
|
2534
|
+
description: Whether to show goal lines on the graph.
|
|
2535
|
+
badges:
|
|
2536
|
+
type: array
|
|
2537
|
+
items:
|
|
2538
|
+
type: string
|
|
2539
|
+
description: Earned badge IDs to include in the shared graph.
|
|
2540
|
+
required:
|
|
2541
|
+
- tag_ids
|
|
2542
|
+
- title
|
|
2543
|
+
User:
|
|
2544
|
+
type: object
|
|
2545
|
+
properties:
|
|
2546
|
+
id:
|
|
2547
|
+
type: integer
|
|
2548
|
+
description: User ID
|
|
2549
|
+
email:
|
|
2550
|
+
type: string
|
|
2551
|
+
format: email
|
|
2552
|
+
description: User email address
|
|
2553
|
+
name:
|
|
2554
|
+
type: string
|
|
2555
|
+
nullable: true
|
|
2556
|
+
description: Display name
|
|
2557
|
+
language:
|
|
2558
|
+
type: string
|
|
2559
|
+
nullable: true
|
|
2560
|
+
description: Preferred language
|
|
2561
|
+
plan:
|
|
2562
|
+
type: string
|
|
2563
|
+
description: >-
|
|
2564
|
+
Plan for this service, derived from the account's subscriptions.
|
|
2565
|
+
`free` when the account holds no active subscription for Recorder.
|
|
2566
|
+
subscription_status:
|
|
2567
|
+
type: string
|
|
2568
|
+
nullable: true
|
|
2569
|
+
description: Subscription status for that subscription, if any
|
|
2570
|
+
required:
|
|
2571
|
+
- id
|
|
2572
|
+
- email
|
|
2573
|
+
Goal:
|
|
2574
|
+
type: object
|
|
2575
|
+
properties:
|
|
2576
|
+
id:
|
|
2577
|
+
type: integer
|
|
2578
|
+
user_id:
|
|
2579
|
+
type: integer
|
|
2580
|
+
tag_id:
|
|
2581
|
+
type: integer
|
|
2582
|
+
goal_type:
|
|
2583
|
+
type: string
|
|
2584
|
+
enum:
|
|
2585
|
+
- goal_value
|
|
2586
|
+
goal_value:
|
|
2587
|
+
type: number
|
|
2588
|
+
nullable: true
|
|
2589
|
+
direction:
|
|
2590
|
+
type: string
|
|
2591
|
+
enum:
|
|
2592
|
+
- above
|
|
2593
|
+
- below
|
|
2594
|
+
- null
|
|
2595
|
+
nullable: true
|
|
2596
|
+
start_date:
|
|
2597
|
+
type: string
|
|
2598
|
+
nullable: true
|
|
2599
|
+
end_date:
|
|
2600
|
+
type: string
|
|
2601
|
+
nullable: true
|
|
2602
|
+
comment:
|
|
2603
|
+
type: string
|
|
2604
|
+
nullable: true
|
|
2605
|
+
virtual_tag_key:
|
|
2606
|
+
type: string
|
|
2607
|
+
nullable: true
|
|
2608
|
+
is_hidden:
|
|
2609
|
+
type: integer
|
|
2610
|
+
description: 0 = visible, 1 = hidden
|
|
2611
|
+
tag_name:
|
|
2612
|
+
type: string
|
|
2613
|
+
tag_unit:
|
|
2614
|
+
type: string
|
|
2615
|
+
nullable: true
|
|
2616
|
+
created_at:
|
|
2617
|
+
type: string
|
|
2618
|
+
format: date-time
|
|
2619
|
+
updated_at:
|
|
2620
|
+
type: string
|
|
2621
|
+
format: date-time
|
|
2622
|
+
required:
|
|
2623
|
+
- id
|
|
2624
|
+
- user_id
|
|
2625
|
+
- tag_id
|
|
2626
|
+
- goal_type
|
|
2627
|
+
- is_hidden
|
|
2628
|
+
- tag_name
|
|
2629
|
+
- created_at
|
|
2630
|
+
- updated_at
|
|
2631
|
+
CreateGoalInput:
|
|
2632
|
+
type: object
|
|
2633
|
+
properties:
|
|
2634
|
+
tag_id:
|
|
2635
|
+
type: integer
|
|
2636
|
+
description: Tag ID to associate with the goal
|
|
2637
|
+
goal_type:
|
|
2638
|
+
type: string
|
|
2639
|
+
enum:
|
|
2640
|
+
- goal_value
|
|
2641
|
+
description: Type of goal
|
|
2642
|
+
goal_value:
|
|
2643
|
+
type: number
|
|
2644
|
+
description: Goal value (for goal_value type)
|
|
2645
|
+
direction:
|
|
2646
|
+
type: string
|
|
2647
|
+
enum:
|
|
2648
|
+
- above
|
|
2649
|
+
- below
|
|
2650
|
+
description: Direction for target value comparison
|
|
2651
|
+
start_date:
|
|
2652
|
+
type: string
|
|
2653
|
+
description: Goal start date
|
|
2654
|
+
end_date:
|
|
2655
|
+
type: string
|
|
2656
|
+
description: Goal end date
|
|
2657
|
+
comment:
|
|
2658
|
+
type: string
|
|
2659
|
+
description: Optional comment
|
|
2660
|
+
virtual_tag_key:
|
|
2661
|
+
type: string
|
|
2662
|
+
description: Virtual tag key
|
|
2663
|
+
required:
|
|
2664
|
+
- tag_id
|
|
2665
|
+
- goal_type
|
|
2666
|
+
UpdateGoalInput:
|
|
2667
|
+
type: object
|
|
2668
|
+
properties:
|
|
2669
|
+
tag_id:
|
|
2670
|
+
type: integer
|
|
2671
|
+
goal_type:
|
|
2672
|
+
type: string
|
|
2673
|
+
enum:
|
|
2674
|
+
- goal_value
|
|
2675
|
+
goal_value:
|
|
2676
|
+
type: number
|
|
2677
|
+
direction:
|
|
2678
|
+
type: string
|
|
2679
|
+
enum:
|
|
2680
|
+
- above
|
|
2681
|
+
- below
|
|
2682
|
+
start_date:
|
|
2683
|
+
type: string
|
|
2684
|
+
end_date:
|
|
2685
|
+
type: string
|
|
2686
|
+
comment:
|
|
2687
|
+
type: string
|
|
2688
|
+
virtual_tag_key:
|
|
2689
|
+
type: string
|
|
2690
|
+
ErrorResponse:
|
|
2691
|
+
type: object
|
|
2692
|
+
properties:
|
|
2693
|
+
success:
|
|
2694
|
+
type: boolean
|
|
2695
|
+
enum:
|
|
2696
|
+
- false
|
|
2697
|
+
example: false
|
|
2698
|
+
error:
|
|
2699
|
+
type: object
|
|
2700
|
+
properties:
|
|
2701
|
+
code:
|
|
2702
|
+
type: string
|
|
2703
|
+
description: Error code
|
|
2704
|
+
message:
|
|
2705
|
+
type: string
|
|
2706
|
+
description: Error message
|
|
2707
|
+
details:
|
|
2708
|
+
type: object
|
|
2709
|
+
description: Additional error details
|
|
2710
|
+
additionalProperties: true
|
|
2711
|
+
responses:
|
|
2712
|
+
ValidationError:
|
|
2713
|
+
description: Validation error
|
|
2714
|
+
content:
|
|
2715
|
+
application/json:
|
|
2716
|
+
schema:
|
|
2717
|
+
$ref: '#/components/schemas/ErrorResponse'
|
|
2718
|
+
example:
|
|
2719
|
+
success: false
|
|
2720
|
+
error:
|
|
2721
|
+
code: VALIDATION_ERROR
|
|
2722
|
+
message: Invalid input data
|
|
2723
|
+
NotFound:
|
|
2724
|
+
description: Resource not found
|
|
2725
|
+
content:
|
|
2726
|
+
application/json:
|
|
2727
|
+
schema:
|
|
2728
|
+
$ref: '#/components/schemas/ErrorResponse'
|
|
2729
|
+
example:
|
|
2730
|
+
success: false
|
|
2731
|
+
error:
|
|
2732
|
+
code: NOT_FOUND
|
|
2733
|
+
message: Record not found
|
|
2734
|
+
Unauthorized:
|
|
2735
|
+
description: Authentication required
|
|
2736
|
+
content:
|
|
2737
|
+
application/json:
|
|
2738
|
+
schema:
|
|
2739
|
+
$ref: '#/components/schemas/ErrorResponse'
|
|
2740
|
+
example:
|
|
2741
|
+
success: false
|
|
2742
|
+
error:
|
|
2743
|
+
code: AUTH_ERROR
|
|
2744
|
+
message: No authentication token provided
|
|
2745
|
+
InternalError:
|
|
2746
|
+
description: Internal server error
|
|
2747
|
+
content:
|
|
2748
|
+
application/json:
|
|
2749
|
+
schema:
|
|
2750
|
+
$ref: '#/components/schemas/ErrorResponse'
|
|
2751
|
+
example:
|
|
2752
|
+
success: false
|
|
2753
|
+
error:
|
|
2754
|
+
code: INTERNAL_ERROR
|
|
2755
|
+
message: An unexpected error occurred
|
|
2756
|
+
tags:
|
|
2757
|
+
- name: Health
|
|
2758
|
+
description: Health check endpoints
|
|
2759
|
+
- name: User
|
|
2760
|
+
description: User profile management
|
|
2761
|
+
- name: Records
|
|
2762
|
+
description: Time-series record management
|
|
2763
|
+
- name: Statistics
|
|
2764
|
+
description: Statistical analysis of records
|
|
2765
|
+
- name: Tags
|
|
2766
|
+
description: Tag management for organizing records
|
|
2767
|
+
- name: Shares
|
|
2768
|
+
description: Data sharing between users
|
|
2769
|
+
- name: Shared Graphs
|
|
2770
|
+
description: Public graph sharing for SNS
|
|
2771
|
+
- name: Media
|
|
2772
|
+
description: Media file upload and management
|
|
2773
|
+
- name: Goals
|
|
2774
|
+
description: Goal tracking and management
|