{"openapi":"3.1.0","info":{"title":"HomePlanner API","version":"0.2.0","description":"Multi-tenant home planner. Authenticate with `Authorization: Bearer <JWT>` (from /auth/login) or a household API key via `X-API-Key`. Endpoints marked admin/superadmin additionally require that role."},"servers":[{"url":"https://homeplanarr.com/api/v1"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}}},"security":[{"bearerAuth":[]},{"apiKey":[]}],"paths":{"/health":{"get":{"summary":"Health & first-run status","tags":["meta"],"security":[],"responses":{"200":{"description":"Success"}}}},"/openapi.json":{"get":{"summary":"This document","tags":["meta"],"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/register":{"post":{"summary":"Register (first user, open signup or invite)","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"},"name":{"type":"string"},"household_name":{"type":"string"},"invite_token":{"type":"string"}},"required":["email","password","name"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/login":{"post":{"summary":"Password login (+ totp_code when MFA is enabled)","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"},"totp_code":{"type":"string"}},"required":["email","password"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/refresh":{"post":{"summary":"Rotate a refresh token for a new JWT","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"refresh_token":{"type":"string"}},"required":["refresh_token"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/logout":{"post":{"summary":"Revoke all refresh tokens","tags":["auth"],"responses":{"200":{"description":"Success"}}}},"/auth/me":{"get":{"summary":"Current user incl. passkeys","tags":["auth"],"responses":{"200":{"description":"Success"}}}},"/auth/change-password":{"post":{"summary":"Change password (logs out other sessions)","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"current_password":{"type":"string"},"new_password":{"type":"string"}},"required":["current_password","new_password"]}}}},"responses":{"200":{"description":"Success"}}}},"/auth/alerts":{"get":{"summary":"Alert preferences","tags":["auth"],"responses":{"200":{"description":"Success"}}},"put":{"summary":"Set alert channel (none/email/discord)","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"alert_channel":{"type":"string"},"discord_webhook_url":{"type":"string"}},"required":["alert_channel"]}}}},"responses":{"200":{"description":"Success"}}}},"/auth/totp/setup":{"post":{"summary":"Start TOTP enrollment (otpauth URI)","tags":["auth"],"responses":{"200":{"description":"Success"}}}},"/auth/totp/enable":{"post":{"summary":"Verify a code and enable MFA","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"Success"}}}},"/auth/totp/disable":{"post":{"summary":"Disable MFA (requires a valid code)","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"Success"}}}},"/auth/passkeys/register/options":{"post":{"summary":"WebAuthn registration options","tags":["auth"],"responses":{"200":{"description":"Success"}}}},"/auth/passkeys/register/verify":{"post":{"summary":"Store a new passkey","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"challenge_token":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"response":{"type":"object"}},"required":["challenge_token","id","response"]}}}},"responses":{"200":{"description":"Success"}}}},"/auth/passkeys/login/options":{"post":{"summary":"WebAuthn login options","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/passkeys/login/verify":{"post":{"summary":"Passkey login -> JWT","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"challenge_token":{"type":"string"},"id":{"type":"string"},"response":{"type":"object"}},"required":["challenge_token","id","response"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/passkeys":{"get":{"summary":"List my passkeys","tags":["auth"],"responses":{"200":{"description":"Success"}}}},"/auth/passkeys/{id}":{"delete":{"summary":"Remove a passkey","tags":["auth"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/auth/forgot-password":{"post":{"summary":"Request a password-reset mail (rate limited, never leaks accounts)","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/reset-password":{"post":{"summary":"Set a new password with an emailed token","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"new_password":{"type":"string"}},"required":["token","new_password"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/verify-email":{"post":{"summary":"Verify the email address with an emailed token","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"security":[],"responses":{"200":{"description":"Success"}}}},"/auth/resend-verification":{"post":{"summary":"Resend the verification mail","tags":["auth"],"responses":{"200":{"description":"Success"}}}},"/auth/totp/recovery-codes":{"post":{"summary":"Regenerate one-time recovery codes (requires a TOTP code)","tags":["auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"Success"}}}},"/auth/invite/{token}":{"get":{"summary":"Inspect an invite (for the signup form)","tags":["auth"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"security":[],"responses":{"200":{"description":"Success"}}}},"/households/me":{"get":{"summary":"My household","tags":["households"],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Rename household","tags":["households"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete the household and ALL its data (confirm with the household name)","tags":["households"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"string"}},"required":["confirm"]}}}},"responses":{"200":{"description":"Success"}}}},"/households/me/users":{"get":{"summary":"Household accounts","tags":["households"],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create an account","tags":["households"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"password":{"type":"string"},"role":{"type":"string"}},"required":["email","name","password"]}}}},"responses":{"200":{"description":"Success"}}}},"/households/me/users/{id}":{"patch":{"summary":"Update an account","tags":["households"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"},"active":{"type":"boolean"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete an account","tags":["households"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/households/me/users/{id}/reset-mfa":{"post":{"summary":"Reset a member's MFA (clears TOTP + recovery codes)","tags":["households"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/households/me/invites":{"get":{"summary":"Pending household invites","tags":["households"],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Invite someone to this household by email (they pick their own name)","tags":["households"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"role":{"type":"string"},"expires_days":{"type":"number"}},"required":["email"]}}}},"responses":{"200":{"description":"Success"}}}},"/households/me/invites/{id}":{"delete":{"summary":"Revoke a pending invite","tags":["households"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/households/me/permissions":{"get":{"summary":"Sections role=member users may see","tags":["households"],"responses":{"200":{"description":"Success"}}},"put":{"summary":"Set which sections members may see (economy/chores/renovations/food)","tags":["households"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"member_permissions":{"type":"string[]"}},"required":["member_permissions"]}}}},"responses":{"200":{"description":"Success"}}}},"/households/me/housing":{"get":{"summary":"Housing profile (hyra vs loans + fastighetsskatt)","tags":["households"],"responses":{"200":{"description":"Success"}}},"put":{"summary":"Set housing type, rent/property tax, Nord Pool area, currency, pay-reminder day and water price","tags":["households"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"housing_type":{"type":"string"},"monthly_rent":{"type":"number"},"property_tax_yearly":{"type":"number"},"price_area":{"type":"string"},"currency":{"type":"string"},"split_notify_day":{"type":"number"},"water_price_per_m3":{"type":"number"}},"required":["housing_type"]}}}},"responses":{"200":{"description":"Success"}}}},"/households/me/cost-split":{"get":{"summary":"Monthly cost split per member (percent or fixed shares, assigned + remainder)","tags":["households"],"responses":{"200":{"description":"Success"}}}},"/households/me/export":{"get":{"summary":"Full household data export (JSON)","tags":["households"],"responses":{"200":{"description":"Success"}}}},"/households/me/api-keys":{"get":{"summary":"List API keys","tags":["households"],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create an API key (full key returned once)","tags":["households"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}}},"/households/me/api-keys/{id}":{"delete":{"summary":"Revoke an API key","tags":["households"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/admin/stats":{"get":{"summary":"Instance stats","tags":["admin"],"responses":{"200":{"description":"Success"}}}},"/admin/households":{"get":{"summary":"All households + billing","tags":["admin"],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a household","tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}}},"/admin/households/{id}/billing":{"put":{"summary":"Billing on/off per household","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"billing_exempt":{"type":"boolean"}},"required":["billing_exempt"]}}}},"responses":{"200":{"description":"Success"}}}},"/admin/households/{id}/subscription":{"put":{"summary":"Override subscription (comp/extend/shut off)","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"current_period_end":{"type":"string"}},"required":["status"]}}}},"responses":{"200":{"description":"Success"}}}},"/admin/subscriptions":{"get":{"summary":"All subscriptions","tags":["admin"],"responses":{"200":{"description":"Success"}}}},"/admin/users":{"get":{"summary":"All users","tags":["admin"],"responses":{"200":{"description":"Success"}}}},"/admin/users/{id}":{"patch":{"summary":"Activate/deactivate, promote","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"},"is_superadmin":{"type":"boolean"},"role":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}}},"/admin/invites":{"get":{"summary":"List invites","tags":["admin"],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Invite a user (emailed when mail is configured)","tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"household_id":{"type":"number"},"household_name":{"type":"string"},"role":{"type":"string"},"make_superadmin":{"type":"boolean"},"expires_days":{"type":"number"}},"required":["email"]}}}},"responses":{"200":{"description":"Success"}}}},"/admin/invites/{id}":{"delete":{"summary":"Revoke an invite","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/admin/audit":{"get":{"summary":"Audit log of privileged actions (paginated)","tags":["admin"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/admin/settings/instance":{"get":{"summary":"Runtime instance config + env defaults","tags":["admin"],"responses":{"200":{"description":"Success"}}},"put":{"summary":"Update runtime config (signup, billing, trial, currency...)","tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"open_signup":{"type":"boolean"},"billing_enabled":{"type":"boolean"},"trial_days":{"type":"number"},"access_ttl_seconds":{"type":"number"},"currency":{"type":"string"},"price_area":{"type":"string"},"app_url":{"type":"string"},"house_name":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}}},"/admin/settings":{"get":{"summary":"Mail + Discord settings","tags":["admin"],"responses":{"200":{"description":"Success"}}}},"/admin/settings/mail":{"put":{"summary":"Configure the mail provider","tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string"},"from":{"type":"string"},"api_url":{"type":"string"},"api_key":{"type":"string"}},"required":["provider","from"]}}}},"responses":{"200":{"description":"Success"}}}},"/admin/settings/discord":{"put":{"summary":"Configure the Discord bot","tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"bot_name":{"type":"string"},"default_webhook_url":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}}},"/admin/settings/test-mail":{"post":{"summary":"Send a test mail","tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string"}},"required":["to"]}}}},"responses":{"200":{"description":"Success"}}}},"/admin/settings/test-discord":{"post":{"summary":"Send a test Discord message","tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"webhook_url":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}}},"/billing/status":{"get":{"summary":"Subscription & access state","tags":["billing"],"responses":{"200":{"description":"Success"}}}},"/billing/subscribe":{"post":{"summary":"Create a PayPal subscription -> approve_url","tags":["billing"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"return_url":{"type":"string"},"cancel_url":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}}},"/billing/cancel":{"post":{"summary":"Cancel the PayPal subscription","tags":["billing"],"responses":{"200":{"description":"Success"}}}},"/billing/webhook/paypal":{"post":{"summary":"PayPal webhook receiver","tags":["billing"],"security":[],"responses":{"200":{"description":"Success"}}}},"/members":{"get":{"summary":"List members (paginated, total in X-Total-Count)","tags":["members"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a member","tags":["members"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"split_mode":{"type":"string"},"split_value":{"type":"number"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}}},"/members/{id}":{"get":{"summary":"Get one","tags":["members"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["members"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"split_mode":{"type":"string"},"split_value":{"type":"number"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["members"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/receipts":{"get":{"summary":"List receipts (paginated, total in X-Total-Count)","tags":["receipts"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a receipt","tags":["receipts"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"purchased_at":{"type":"string","format":"date"},"amount":{"type":"number"},"store":{"type":"string"},"description":{"type":"string"},"currency":{"type":"string"},"category":{"type":"string"},"warranty_months":{"type":"number"},"member_id":{"type":"number"},"improvement_id":{"type":"number"},"notes":{"type":"string"}},"required":["purchased_at","amount"]}}}},"responses":{"200":{"description":"Success"}}}},"/receipts/{id}":{"get":{"summary":"Get one","tags":["receipts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["receipts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"purchased_at":{"type":"string","format":"date"},"amount":{"type":"number"},"store":{"type":"string"},"description":{"type":"string"},"currency":{"type":"string"},"category":{"type":"string"},"warranty_months":{"type":"number"},"member_id":{"type":"number"},"improvement_id":{"type":"number"},"notes":{"type":"string"}},"required":["purchased_at","amount"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["receipts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/receipts/warranties":{"get":{"summary":"Warranties still running","tags":["receipts"],"responses":{"200":{"description":"Success"}}}},"/receipts/{id}/attachment":{"post":{"summary":"Upload a receipt photo (multipart 'file')","tags":["receipts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"get":{"summary":"Download the attachment","tags":["receipts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/receipts/{id}/ocr":{"post":{"summary":"OCR the attachment (?apply=true fills empty fields)","tags":["receipts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"apply","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/improvements":{"get":{"summary":"List improvements (paginated, total in X-Total-Count)","tags":["improvements"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a improvement","tags":["improvements"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"room_id":{"type":"number"},"category":{"type":"string"},"status":{"type":"string"},"planned_for":{"type":"string","format":"date"},"completed_at":{"type":"string","format":"date"},"cost":{"type":"number"},"budget":{"type":"number"},"lifetime_years":{"type":"number"},"vendor":{"type":"string"},"notes":{"type":"string"}},"required":["title"]}}}},"responses":{"200":{"description":"Success"}}}},"/improvements/{id}":{"get":{"summary":"Get one","tags":["improvements"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["improvements"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"room_id":{"type":"number"},"category":{"type":"string"},"status":{"type":"string"},"planned_for":{"type":"string","format":"date"},"completed_at":{"type":"string","format":"date"},"cost":{"type":"number"},"budget":{"type":"number"},"lifetime_years":{"type":"number"},"vendor":{"type":"string"},"notes":{"type":"string"}},"required":["title"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["improvements"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/improvements/timeline":{"get":{"summary":"Renovation timeline (newest first) with room/property names and bound photos + documents","tags":["improvements"],"responses":{"200":{"description":"Success"}}}},"/improvements/end-of-life":{"get":{"summary":"Lifetime running out within N years","tags":["improvements"],"parameters":[{"name":"within_years","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/chores":{"get":{"summary":"List chores (paginated, total in X-Total-Count)","tags":["chores"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a chore","tags":["chores"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"points":{"type":"number"},"interval_days":{"type":"number"},"active":{"type":"boolean"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}}},"/chores/{id}":{"get":{"summary":"Get one","tags":["chores"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["chores"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"points":{"type":"number"},"interval_days":{"type":"number"},"active":{"type":"boolean"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["chores"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/chores/{id}/complete":{"post":{"summary":"Mark done by a member (awards points)","tags":["chores"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"number"},"completed_at":{"type":"string","format":"date-time"},"comment":{"type":"string"}},"required":["member_id"]}}}},"responses":{"200":{"description":"Success"}}}},"/chores/leaderboard":{"get":{"summary":"Points leaderboard","tags":["chores"],"parameters":[{"name":"since","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/chores/completions":{"get":{"summary":"Completion history","tags":["chores"],"parameters":[{"name":"member_id","in":"query","schema":{"type":"string"}},{"name":"chore_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/expenses":{"get":{"summary":"List expenses (paginated, total in X-Total-Count)","tags":["expenses"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a expense","tags":["expenses"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"},"category":{"type":"string"},"provider":{"type":"string"},"period":{"type":"string"},"due_day":{"type":"number"},"start_date":{"type":"string","format":"date"},"contract_end":{"type":"string","format":"date"},"auto_renews":{"type":"boolean"},"notes":{"type":"string"},"active":{"type":"boolean"}},"required":["name","amount"]}}}},"responses":{"200":{"description":"Success"}}}},"/expenses/{id}":{"get":{"summary":"Get one","tags":["expenses"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["expenses"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"},"category":{"type":"string"},"provider":{"type":"string"},"period":{"type":"string"},"due_day":{"type":"number"},"start_date":{"type":"string","format":"date"},"contract_end":{"type":"string","format":"date"},"auto_renews":{"type":"boolean"},"notes":{"type":"string"},"active":{"type":"boolean"}},"required":["name","amount"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["expenses"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/expenses/renewals":{"get":{"summary":"Contracts renewing within N days","tags":["expenses"],"parameters":[{"name":"within_days","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/expenses/summary":{"get":{"summary":"Monthly running cost by category","tags":["expenses"],"responses":{"200":{"description":"Success"}}}},"/loans":{"get":{"summary":"List loans (paginated, total in X-Total-Count)","tags":["loans"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a loan","tags":["loans"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"current_balance":{"type":"number"},"interest_rate":{"type":"number"},"lender":{"type":"string"},"loan_number":{"type":"string"},"original_amount":{"type":"number"},"rate_type":{"type":"string"},"term_end":{"type":"string","format":"date"},"monthly_amortization":{"type":"number"},"notes":{"type":"string"},"active":{"type":"boolean"}},"required":["name","current_balance","interest_rate"]}}}},"responses":{"200":{"description":"Success"}}}},"/loans/{id}":{"get":{"summary":"Get one","tags":["loans"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["loans"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"current_balance":{"type":"number"},"interest_rate":{"type":"number"},"lender":{"type":"string"},"loan_number":{"type":"string"},"original_amount":{"type":"number"},"rate_type":{"type":"string"},"term_end":{"type":"string","format":"date"},"monthly_amortization":{"type":"number"},"notes":{"type":"string"},"active":{"type":"boolean"}},"required":["name","current_balance","interest_rate"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["loans"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/loans/renewals":{"get":{"summary":"Rate bindings ending within N days","tags":["loans"],"parameters":[{"name":"within_days","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/loans/summary":{"get":{"summary":"Balance, weighted rate, monthly cost","tags":["loans"],"responses":{"200":{"description":"Success"}}}},"/energy":{"get":{"summary":"List readings","tags":["energy"],"parameters":[{"name":"start","in":"query","schema":{"type":"string"}},{"name":"end","in":"query","schema":{"type":"string"}},{"name":"sensor_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Add a reading (auto-priced from spot prices)","tags":["energy"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ts":{"type":"string","format":"date-time"},"kwh":{"type":"number"},"cost":{"type":"number"},"price_per_kwh":{"type":"number"},"source":{"type":"string"},"sensor_id":{"type":"string"}},"required":["kwh"]}}}},"responses":{"200":{"description":"Success"}}}},"/energy/bulk":{"post":{"summary":"Bulk import readings","tags":["energy"],"responses":{"200":{"description":"Success"}}}},"/energy/stats":{"get":{"summary":"kWh + cost per day/month/year","tags":["energy"],"parameters":[{"name":"group_by","in":"query","schema":{"type":"string"}},{"name":"start","in":"query","schema":{"type":"string"}},{"name":"end","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/energy/prices/refresh":{"post":{"summary":"Fetch Nord Pool day-ahead prices","tags":["energy"],"parameters":[{"name":"day","in":"query","schema":{"type":"string"}},{"name":"area","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/energy/prices":{"get":{"summary":"Stored spot prices for a day","tags":["energy"],"parameters":[{"name":"day","in":"query","schema":{"type":"string"}},{"name":"area","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/energy/prices/current":{"get":{"summary":"Spot price for the current hour","tags":["energy"],"parameters":[{"name":"area","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/energy/{id}":{"delete":{"summary":"Delete a reading","tags":["energy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/metrics":{"get":{"summary":"List home metrics (water, solar, runtime, radon, co2, ...)","tags":["metrics"],"parameters":[{"name":"kind","in":"query","schema":{"type":"string"}},{"name":"entity_id","in":"query","schema":{"type":"string"}},{"name":"room_id","in":"query","schema":{"type":"string"}},{"name":"start","in":"query","schema":{"type":"string"}},{"name":"end","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Add metric reading(s); water is costed from the water price, solar valued at the spot price","tags":["metrics"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string"},"value":{"type":"number"},"ts":{"type":"string","format":"date-time"},"entity_id":{"type":"string"},"room_id":{"type":"number"},"room":{"type":"string"},"unit":{"type":"string"}},"required":["kind","value"]}}}},"responses":{"200":{"description":"Success"}}}},"/metrics/stats":{"get":{"summary":"Per day/month totals (water/solar/runtime) or averages (radon/co2/...)","tags":["metrics"],"parameters":[{"name":"kind","in":"query","schema":{"type":"string"}},{"name":"group_by","in":"query","schema":{"type":"string"}},{"name":"entity_id","in":"query","schema":{"type":"string"}},{"name":"start","in":"query","schema":{"type":"string"}},{"name":"end","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/metrics/latest":{"get":{"summary":"Latest reading per kind + room + entity","tags":["metrics"],"responses":{"200":{"description":"Success"}}}},"/metrics/{id}":{"delete":{"summary":"Delete a reading","tags":["metrics"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/devices":{"get":{"summary":"Devices (smoke/leak detectors...) with battery health","tags":["devices"],"responses":{"200":{"description":"Success"}}}},"/devices/{id}":{"patch":{"summary":"Rename / reclassify a device","tags":["devices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"kind":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Forget a device","tags":["devices"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/budgets":{"get":{"summary":"List budgets (paginated, total in X-Total-Count)","tags":["budgets"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a budget","tags":["budgets"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"year":{"type":"number"},"category":{"type":"string"},"amount":{"type":"number"},"notes":{"type":"string"}},"required":["year","category","amount"]}}}},"responses":{"200":{"description":"Success"}}}},"/budgets/{id}":{"get":{"summary":"Get one","tags":["budgets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["budgets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"year":{"type":"number"},"category":{"type":"string"},"amount":{"type":"number"},"notes":{"type":"string"}},"required":["year","category","amount"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["budgets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/recipes":{"get":{"summary":"List recipes (paginated, total in X-Total-Count)","tags":["recipes"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a recipe","tags":["recipes"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"category":{"type":"string"},"rating":{"type":"number"},"notes":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}}},"/recipes/{id}":{"get":{"summary":"Get one","tags":["recipes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["recipes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"category":{"type":"string"},"rating":{"type":"number"},"notes":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["recipes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/food":{"get":{"summary":"List food (paginated, total in X-Total-Count)","tags":["food"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a foo","tags":["food"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"brand":{"type":"string"},"barcode":{"type":"string"},"quantity":{"type":"number"},"unit":{"type":"string"},"location":{"type":"string"},"expires_at":{"type":"string","format":"date"},"image_url":{"type":"string"},"notes":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}}},"/food/{id}":{"get":{"summary":"Get one","tags":["food"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["food"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"brand":{"type":"string"},"barcode":{"type":"string"},"quantity":{"type":"number"},"unit":{"type":"string"},"location":{"type":"string"},"expires_at":{"type":"string","format":"date"},"image_url":{"type":"string"},"notes":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["food"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/food/lookup/{barcode}":{"get":{"summary":"Look an EAN up on Open Food Facts (name, brand, size, photo)","tags":["food"],"responses":{"200":{"description":"Success"}}}},"/food/scan":{"post":{"summary":"Scan an EAN: bumps the quantity of a known item, otherwise looks it up and adds it","tags":["food"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"barcode":{"type":"string"}},"required":["barcode"]}}}},"responses":{"200":{"description":"Success"}}}},"/budgets/status":{"get":{"summary":"Budget vs actual per category","tags":["budgets"],"parameters":[{"name":"year","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/dashboard/summary":{"get":{"summary":"Everything for a dashboard in one call","tags":["dashboard"],"responses":{"200":{"description":"Success"}}}},"/simulator":{"get":{"summary":"Project loans, inflated running costs and planned renovations over N years","tags":["simulator"],"parameters":[{"name":"years","in":"query","schema":{"type":"string"}},{"name":"inflation","in":"query","schema":{"type":"string"}},{"name":"interest_delta","in":"query","schema":{"type":"string"}},{"name":"inflate_renovations","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/integrations/home-assistant/energy":{"post":{"summary":"HA energy webhook","tags":["home-assistant"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"entity_id":{"type":"string"},"kwh":{"type":"number"},"ts":{"type":"string","format":"date-time"},"price_per_kwh":{"type":"number"}},"required":["kwh"]}}}},"responses":{"200":{"description":"Success"}}}},"/integrations/home-assistant/chore-event":{"post":{"summary":"Log a chore by name (NFC/buttons/voice)","tags":["home-assistant"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"chore":{"type":"string"},"member":{"type":"string"},"comment":{"type":"string"}},"required":["chore","member"]}}}},"responses":{"200":{"description":"Success"}}}},"/integrations/home-assistant/sensors":{"get":{"summary":"Flat sensor document for HA REST sensors (incl. water/solar month + low batteries)","tags":["home-assistant"],"responses":{"200":{"description":"Success"}}}},"/integrations/home-assistant/metric":{"post":{"summary":"Generic sensor webhook: one reading or an array (kind, value, room by name ok)","tags":["home-assistant"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string"},"value":{"type":"number"},"ts":{"type":"string","format":"date-time"},"entity_id":{"type":"string"},"room":{"type":"string"},"unit":{"type":"string"}},"required":["kind","value"]}}}},"responses":{"200":{"description":"Success"}}}},"/integrations/home-assistant/device-status":{"post":{"summary":"Battery/health report for smoke & leak detectors etc","tags":["home-assistant"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"entity_id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"battery_pct":{"type":"number"}},"required":["entity_id"]}}}},"responses":{"200":{"description":"Success"}}}},"/properties":{"get":{"summary":"List properties (house/apartment objects)","tags":["properties"],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a property (address, fastighetsbeteckning, apartment no)","tags":["properties"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"fastighetsbeteckning":{"type":"string"},"is_apartment":{"type":"boolean"},"apartment_number":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}}},"/properties/{id}":{"patch":{"summary":"Update a property","tags":["properties"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"fastighetsbeteckning":{"type":"string"},"is_apartment":{"type":"boolean"},"apartment_number":{"type":"string"}}}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete (renovations/receipts are unbound, not deleted)","tags":["properties"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/properties/{id}/transfer":{"post":{"summary":"Transfer the property + history to another user by email (invites non-customers)","tags":["properties"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]}}}},"responses":{"200":{"description":"Success"}}}},"/properties/transfers":{"get":{"summary":"My incoming + outgoing pending transfers","tags":["properties"],"responses":{"200":{"description":"Success"}}}},"/properties/transfers/{token}/accept":{"post":{"summary":"Accept a transfer into my household","tags":["properties"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/properties/transfers/{token}/cancel":{"post":{"summary":"Cancel an outgoing transfer","tags":["properties"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}}},"/rooms":{"get":{"summary":"List rooms (paginated, total in X-Total-Count)","tags":["rooms"],"parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Create a room","tags":["rooms"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"size_m2":{"type":"number"},"description":{"type":"string"},"property_id":{"type":"number"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}}},"/rooms/{id}":{"get":{"summary":"Get one","tags":["rooms"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}},"patch":{"summary":"Update","tags":["rooms"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"size_m2":{"type":"number"},"description":{"type":"string"},"property_id":{"type":"number"}},"required":["name"]}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete","tags":["rooms"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/documents":{"get":{"summary":"List documents (bygglov, protokoll, drawings, room photos...)","tags":["documents"],"parameters":[{"name":"property_id","in":"query","schema":{"type":"string"}},{"name":"improvement_id","in":"query","schema":{"type":"string"}},{"name":"room_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Upload a document (multipart: file, name?, property_id?, improvement_id?, room_id?)","tags":["documents"],"responses":{"200":{"description":"Success"}}}},"/documents/{id}":{"patch":{"summary":"Rename or re-bind a document","tags":["documents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"property_id":{"type":"number"},"improvement_id":{"type":"number"},"room_id":{"type":"number"}}}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Delete a document","tags":["documents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/documents/{id}/file":{"get":{"summary":"Download the file","tags":["documents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/nfc/tags":{"get":{"summary":"List registered NFC tags","tags":["nfc"],"responses":{"200":{"description":"Success"}}},"post":{"summary":"Register a tag and link it to a chore","tags":["nfc"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tag_id":{"type":"string"},"name":{"type":"string"},"chore_id":{"type":"number"},"member_id":{"type":"number"}},"required":["tag_id"]}}}},"responses":{"200":{"description":"Success"}}}},"/nfc/tags/{id}":{"patch":{"summary":"Relink or rename a tag","tags":["nfc"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"chore_id":{"type":"number"},"member_id":{"type":"number"}}}}}},"responses":{"200":{"description":"Success"}}},"delete":{"summary":"Remove a tag","tags":["nfc"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success"}}}},"/nfc/scan":{"post":{"summary":"A physical scan: completes the linked chore","tags":["nfc"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tag_id":{"type":"string"},"member":{"type":"string"},"comment":{"type":"string"}},"required":["tag_id"]}}}},"responses":{"200":{"description":"Success"}}}},"/calendar.ics":{"get":{"summary":"iCal family planner feed (?token=)","tags":["calendar"],"parameters":[{"name":"token","in":"query","schema":{"type":"string"}}],"security":[],"responses":{"200":{"description":"Success"}}}}}}