Husvo

Utvecklare

Husvos API och MCP-server

Allt som går att göra i webben går att göra med ett anrop: läsa underhållet, skriva i journalen, lägga till en idé. Ett JSON-API för appar, och en MCP-server för AI-assistenter som Claude. Båda ser samma hus med samma rätt som i webben.

Kom igång API-referens MCP och Claude Ändringar

Två ingångar

APIMCP
Adresshttps://husvo.app/api/v1https://husvo.app/mcp
FörAppar och skript. Husvos iOS-app, som är under arbete, bygger på samma API.AI-assistenter: Claude i webben, Claude Code, Claude Desktop och andra som talar MCP.
FormatJSON över HTTPS, kontraktet i OpenAPI 3.0JSON-RPC över Streamable HTTP, utan sessioner
InloggningEnhetstokenEnhetstoken, personlig nyckel eller OAuth
Innehåll131 operationer i 29 delarEtt verktyg per operation, och några skrivna för en assistent

Kontraktet ligger öppet på /openapi/v1.json. Generera en klient ur det, eller läs referensen, som är byggd ur samma fil.

Inloggning

Varje anrop bär en token i Authorization: Bearer …. Kakan från webben gäller aldrig, varken i API:t eller på /mcp.

Enhetstoken, för API:t och MCP

Be om en kod med POST /api/v1/auth/code. Koden kommer med mejl och gäller i tio minuter. Byt den mot en token med POST /api/v1/auth/token. Tokenen börjar med hk_live_ och gäller i 180 dagar efter senaste användningen. Den syns under enheter i husprofilen och kan loggas ut där. Kom igång visar hela vägen med curl.

Personlig nyckel, bara för MCP

Skapa en nyckel i husprofilen under Koppla en AI-assistent. Den visas en gång. Nyckeln gäller bara på /mcp och får 401 i API:t: en assistent ska inte nå mer än verktygen.

OAuth 2.1, för anslutningar i claude.ai

Husvo är sin egen auktoriseringsserver. Klienten hittar den själv: ett anrop utan token till /mcp ger 401 med resource_metadata i WWW-Authenticate. Bara koder med PKCE (S256), bara publika klienter och dynamisk registrering på /oauth/register. Den som loggar in godkänner på en egen samtyckessida i Husvo. En access-token gäller en timme och förnyas med en refresh-token som byts vid varje förnyelse.

AdressVad
/.well-known/oauth-protected-resourceMetadata för resursen (RFC 9728)
/.well-known/oauth-authorization-serverMetadata för auktoriseringsservern (RFC 8414)
POST /oauth/registerDynamisk registrering (RFC 7591)
GET /oauth/authorizeSamtycket, med code_challenge och resource
POST /oauth/tokenKod och refresh-token mot access-token

Scope är hus: läsa och ändra i husen kontot är med i, så långt rollen räcker.

Rättigheter

En token har samma rätt som personen den hör till. Ett hus du inte är med i svarar 404, precis som ett hus som inte finns, så att ingen kan räkna fram vilka hus-id som finns. Kan du läsa men inte ändra blir svaret 403 med koden read_only. Referensen säger vid varje operation vilken rätt som krävs.

Versioner och löftet

API:t har version 1.0 och ligger under /api/v1. Inom v1 ändras det bara genom att något läggs till: nya operationer, nya fält i svaren och nya valfria fält i kropparna. Inget tas bort och inget byter namn eller typ. En app som byggts mot v1 ska fungera även när den inte uppdaterats på länge.

  • Läs svaren tolerant: okända fält ska hoppas över, inte ge fel.
  • Fält som saknar värde skickas inte alls. Behandla ett saknat fält som null.
  • Behövs en ändring som inte går att göra så blir det /api/v2, bredvid v1.
  • GET /api/v1/meta säger vilken lägsta klientversion servern tar. En äldre klient som skickar X-Client-Version får 426 med koden client_too_old.

Allt som lagts till står i ändringsloggen, med datum.

Format

  • Fältnamn i camelCase. Enum-värden som strängar.
  • Tider i UTC med hela sekunder och Z, datum som ÅÅÅÅ-MM-DD.
  • Idéernas kostnader i tusentals kronor (costMinTkr), allt annat i hela kronor (costKr).
  • Listor som kan bli långa har cursor och limit, och svaret har nextCursor tills det är slut.

Felen

Varje fel är application/problem+json enligt RFC 9457. Växla på code, som aldrig byter namn. title och detail är svensk text som går att visa som den är. errors har meddelandet per fält vid 422, och current har den aktuella versionen vid 412. Skicka med traceId om du hör av dig.

{
  "type": "https://husakten.tillim.xyz/api/errors/read_only",
  "title": "Det här får du inte göra",
  "status": 403,
  "detail": "Du kan läsa men inte ändra i huset.",
  "code": "read_only",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}
StatuscodeBetyder
400bad_requestKroppen gick inte att läsa som json, eller ett fält hade fel typ.
401unauthenticatedIngen eller ogiltig token. Tokenen är död: logga in igen.
403read_onlyDu kan läsa men inte ändra i huset.
403forbiddenRollen räcker inte, till exempel för att ändra medlemmar.
403plan_limitPlanen rymmer inte mer. Svaret säger vilket tak.
403confirmation_requiredRadering av kontot kräver en färsk kod i X-Confirm-Code.
403invalid_confirmationKoden i X-Confirm-Code stämde inte eller har gått ut.
404not_foundFinns inte, eller hör till ett hus du inte är med i. Samma svar i båda fallen.
409already_memberPersonen är redan med i huset.
409house_fullHuset har så många medlemmar som planen rymmer.
409invite_closedInbjudan är redan besvarad, återkallad eller utgången.
409owner_fixedÄgaren går inte att ändra eller ta bort.
409active_subscriptionKontot betalar för Plus. Säg upp i kundportalen innan kontot raderas.
409owns_shared_houseKontot äger ett hus som andra är med i. Lämna över det först.
409trial_usedHuset har redan provat Plus.
409trial_not_neededHuset har redan Plus.
409ai_busyEtt svar från AI:n pågår redan för huset.
409in_progressEtt anrop med samma Idempotency-Key pågår fortfarande.
409replay_unavailableSvaret till den här Idempotency-Key finns inte kvar att upprepa.
410goneResursen har gått ut eller ligger i arkivet.
412precondition_failedNågon ändrade resursen efter att du läste den. current har den nya versionen.
413payload_too_largeAnropet eller filen är större än taket.
415unsupported_media_typeFel innehållstyp. Läs referensen för vad operationen tar.
422validationEtt eller flera fält är fel. errors har meddelandet per fält.
422invalid_codeInloggningskoden stämde inte. Tokenen och inloggningen påverkas inte.
422name_requiredEtt nytt konto behöver ett namn. Skicka samma kod igen med name.
403signup_closedHusvo tar inte emot nya konton just nu. Den som har ett konto loggar in som vanligt.
422idempotency_key_reuseSamma Idempotency-Key skickades med ett annat innehåll.
422ai_refusedAI:n svarar inte på den frågan.
426client_too_oldAppen är äldre än minClientVersion och behöver uppdateras.
428precondition_requiredÄndringen kräver If-Match med versionen du läste.
429rate_limitedFör många anrop. Vänta det antal sekunder som Retry-After säger.
429quota_exhaustedHusets AI-frågor för månaden är slut.
500server_errorFel hos oss. Skicka med traceId om du hör av dig.
502ai_failedAI-tjänsten svarade inte. Försök igen om en stund.
503ai_disabledAI är avstängt på servern. Dölj ytan i appen.

En felkod som inte står här kan komma i en senare version. Behandla den efter statuskoden. Adressen i type är ett namn och inte en sida.

Takt

Över taket blir svaret 429 med koden rate_limited och Retry-After i sekunder. Taken räknas i fasta fönster och börjar om när servern startar om.

VadTakRäknas per
Alla anrop till API:t och /mcp300 per minutEnhet eller anslutning
Skrivningar60 per minutEnhet i API:t, användare i MCP
POST /auth/code10 per timmeIP-adress
POST /auth/token20 per timmeIP-adress
POST /oauth/register30 per timmeIP-adress
POST /oauth/token600 per timmeIP-adress

Frågorna till AI:n har dessutom ett tak per hus och månad, som planen sätter. GET …/ask/quota säger hur många som är kvar.

Idempotens

Mobilnät tappar svar. Skicka Idempotency-Key med en egen nyckel, högst 64 tecken, på de POST som skapar något, så blir ett omförsök inte en dubblett. Samma nyckel inom ett dygn ger samma svar igen. Samma nyckel med ett annat innehåll ger 422 idempotency_key_reuse, och medan det första anropet pågår blir det 409 in_progress. Bara lyckade svar sparas. Referensen säger vilka operationer som tar nyckeln.

ETag och If-Match

Resurser som flera i hushållet kan ändra samtidigt har ett versionsnummer. Svaret bär det som ETag: "3" och fältet version. Skicka tillbaka det i If-Match när du ändrar. Har någon annan hunnit före blir det 412 precondition_failed med den nya versionen i current, och utan If-Match blir det 428. Ingen ändring skrivs över utan att någon märker det.

Sidan hakade upp sig.Ladda om🗙