178 lines
2.8 KiB
Markdown
178 lines
2.8 KiB
Markdown
# api contract
|
|
|
|
base url example:
|
|
|
|
```text
|
|
http://jeeves.mother:8765
|
|
```
|
|
|
|
all write endpoints require:
|
|
|
|
```text
|
|
authorization: bearer <phone_capture_token>
|
|
content-type: application/json
|
|
```
|
|
|
|
## get /health
|
|
|
|
request:
|
|
|
|
```http
|
|
GET /health
|
|
```
|
|
|
|
response:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"service": "synq",
|
|
"version": "0.1.0"
|
|
}
|
|
```
|
|
|
|
## post /capture
|
|
|
|
request:
|
|
|
|
```http
|
|
POST /capture
|
|
Authorization: Bearer <token>
|
|
Content-Type: application/json
|
|
```
|
|
|
|
body:
|
|
|
|
```json
|
|
{
|
|
"id": "phone-20260517-143122-a8f2",
|
|
"created_at": "2026-05-17T14:31:22-04:00",
|
|
"kind": "todo",
|
|
"body": "buy printer paper",
|
|
"tags": ["home", "errands"],
|
|
"device": "android"
|
|
}
|
|
```
|
|
|
|
field rules:
|
|
|
|
- `id`: required, stable, unique per capture.
|
|
- `created_at`: required iso-8601 datetime with timezone.
|
|
- `kind`: required, either `note` or `todo`.
|
|
- `body`: required, trimmed, non-empty.
|
|
- `tags`: required array, may be empty.
|
|
- `device`: required string, e.g. `android`.
|
|
|
|
success, newly accepted:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"status": "accepted",
|
|
"id": "phone-20260517-143122-a8f2"
|
|
}
|
|
```
|
|
|
|
success, duplicate/idempotent retry:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"status": "already_seen",
|
|
"id": "phone-20260517-143122-a8f2"
|
|
}
|
|
```
|
|
|
|
invalid token:
|
|
|
|
```json
|
|
{
|
|
"detail": "unauthorized"
|
|
}
|
|
```
|
|
|
|
invalid payload:
|
|
|
|
```json
|
|
{
|
|
"detail": "body must not be empty"
|
|
}
|
|
```
|
|
|
|
## org formatting rules
|
|
|
|
todo capture:
|
|
|
|
```org
|
|
* TODO buy printer paper :home:errands:
|
|
:PROPERTIES:
|
|
:CREATED: [2026-05-17 sun 14:31]
|
|
:SOURCE: android
|
|
:ID: phone-20260517-143122-a8f2
|
|
:END:
|
|
```
|
|
|
|
note capture:
|
|
|
|
```org
|
|
* note :retcon:
|
|
:PROPERTIES:
|
|
:CREATED: [2026-05-17 sun 14:33]
|
|
:SOURCE: android
|
|
:ID: phone-20260517-143322-b91c
|
|
:END:
|
|
|
|
mobile capture should stay dumb and append-only.
|
|
```
|
|
|
|
multiline note capture:
|
|
|
|
input body:
|
|
|
|
```text
|
|
retcon capture idea
|
|
|
|
phone should produce records, not edit org files.
|
|
```
|
|
|
|
output:
|
|
|
|
```org
|
|
* note: retcon capture idea :retcon:
|
|
:PROPERTIES:
|
|
:CREATED: [2026-05-17 sun 14:33]
|
|
:SOURCE: android
|
|
:ID: phone-20260517-143322-b91c
|
|
:END:
|
|
|
|
retcon capture idea
|
|
|
|
phone should produce records, not edit org files.
|
|
```
|
|
|
|
suggested behavior: use first line as heading suffix for notes when helpful, but preserve full body below the drawer.
|
|
|
|
## idempotency
|
|
|
|
server must never append the same `id` twice.
|
|
|
|
recommended implementation:
|
|
|
|
1. begin sqlite transaction.
|
|
2. check whether id already exists.
|
|
3. if exists, return `already_seen`.
|
|
4. append org entry.
|
|
5. insert id into sqlite.
|
|
6. commit.
|
|
|
|
if append succeeds but sqlite insert fails, log loudly. safest implementation may insert first inside a transaction and mark appended after write, but keep the code simple and test the duplicate path.
|
|
|
|
## status codes
|
|
|
|
- 200: accepted or already seen.
|
|
- 400: malformed request or invalid capture.
|
|
- 401: missing/invalid token.
|
|
- 500: server failed to append or persist state.
|
|
|
|
android should treat `accepted` and `already_seen` as synced.
|