openapi: 3.1.0 info: title: Pamietnik API version: 0.1.0 description: Life & travel journal — REST API for trackpoint ingest and data query. servers: - url: http://192.168.1.4:9050 description: NAS (local) - url: http://localhost:9050 description: Local dev security: - cookieAuth: [] paths: /healthz: get: summary: Health check security: [] responses: '200': description: Server is up content: text/plain: example: ok /readyz: get: summary: Readiness check security: [] responses: '200': description: Server is ready # --- Ingest --- /v1/trackpoints: post: summary: Ingest single trackpoint tags: [Ingest] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TrackpointInput' responses: '200': description: Accepted (or duplicate — idempotent) content: application/json: schema: $ref: '#/components/schemas/BatchResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /v1/trackpoints:batch: post: summary: Ingest batch of trackpoints (max 500) tags: [Ingest] requestBody: required: true content: application/json: schema: type: array items: $ref: '#/components/schemas/TrackpointInput' maxItems: 500 responses: '200': description: Processed — check accepted_ids and rejected for details content: application/json: schema: $ref: '#/components/schemas/BatchResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' # --- Query --- /v1/days: get: summary: List days with trackpoint activity tags: [Query] parameters: - name: from in: query required: true schema: type: string format: date example: '2026-01-01' - name: to in: query required: true schema: type: string format: date example: '2026-12-31' responses: '200': description: List of days content: application/json: schema: type: array items: $ref: '#/components/schemas/DaySummary' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /v1/trackpoints: get: summary: List trackpoints for a date tags: [Query] parameters: - name: date in: query required: true schema: type: string format: date example: '2026-04-07' responses: '200': description: List of trackpoints content: application/json: schema: type: array items: $ref: '#/components/schemas/Trackpoint' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /v1/stops: get: summary: List stops for a date tags: [Query] parameters: - name: date in: query required: true schema: type: string format: date example: '2026-04-07' responses: '200': description: List of stops content: application/json: schema: type: array items: $ref: '#/components/schemas/Stop' '401': $ref: '#/components/responses/Unauthorized' /v1/suggestions: get: summary: List suggestions for a date tags: [Query] parameters: - name: date in: query required: true schema: type: string format: date example: '2026-04-07' responses: '200': description: List of suggestions content: application/json: schema: type: array items: $ref: '#/components/schemas/Suggestion' '401': $ref: '#/components/responses/Unauthorized' # --- Journal --- /entries: post: summary: Create journal entry with optional images tags: [Journal] requestBody: required: true content: multipart/form-data: schema: type: object required: [date, time] properties: date: type: string format: date example: '2026-04-07' time: type: string example: '14:30' title: type: string description: type: string lat: type: number format: double lon: type: number format: double images: type: array items: type: string format: binary description: JPEG, PNG, WebP or HEIC — max 10 MB each, 32 MB total responses: '201': description: Entry created content: application/json: schema: $ref: '#/components/schemas/JournalEntry' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' components: securitySchemes: cookieAuth: type: apiKey in: cookie name: session responses: BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' schemas: TrackpointInput: type: object required: [event_id, device_id, trip_id, timestamp, lat, lon] properties: event_id: type: string format: uuid description: Client-generated UUID — used for idempotency device_id: type: string trip_id: type: string timestamp: type: string format: date-time example: '2026-04-07T12:00:00Z' lat: type: number format: double minimum: -90 maximum: 90 lon: type: number format: double minimum: -180 maximum: 180 source: type: string enum: [gps, manual] default: gps note: type: string accuracy_m: type: number format: double speed_mps: type: number format: double bearing_deg: type: number format: double altitude_m: type: number format: double Trackpoint: allOf: - $ref: '#/components/schemas/TrackpointInput' BatchResponse: type: object properties: server_time: type: string format: date-time accepted_ids: type: array items: type: string rejected: type: array items: $ref: '#/components/schemas/RejectedItem' RejectedItem: type: object properties: event_id: type: string code: type: string enum: [VALIDATION_ERROR, DB_ERROR, INVALID_TIMESTAMP] message: type: string DaySummary: type: object properties: date: type: string format: date count: type: integer first_ts: type: string format: date-time last_ts: type: string format: date-time Stop: type: object properties: stop_id: type: string device_id: type: string trip_id: type: string start_ts: type: string format: date-time end_ts: type: string format: date-time center_lat: type: number format: double center_lon: type: number format: double duration_s: type: integer place_label: type: string place_details: type: object Suggestion: type: object properties: suggestion_id: type: string stop_id: type: string type: type: string enum: [highlight, name_place, add_note] title: type: string text: type: string created_at: type: string format: date-time dismissed_at: type: string format: date-time JournalEntry: type: object properties: entry_id: type: string user_id: type: string entry_date: type: string format: date entry_time: type: string example: '14:30' title: type: string description: type: string lat: type: number format: double lon: type: number format: double created_at: type: string format: date-time images: type: array items: $ref: '#/components/schemas/JournalImage' JournalImage: type: object properties: image_id: type: string entry_id: type: string filename: type: string original_name: type: string mime_type: type: string size_bytes: type: integer format: int64 created_at: type: string format: date-time Error: type: object properties: code: type: string message: type: string