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.
Två ingångar
| API | MCP | |
|---|---|---|
| Adress | https://husvo.app/api/v1 | https://husvo.app/mcp |
| För | Appar 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. |
| Format | JSON över HTTPS, kontraktet i OpenAPI 3.0 | JSON-RPC över Streamable HTTP, utan sessioner |
| Inloggning | Enhetstoken | Enhetstoken, personlig nyckel eller OAuth |
| Innehåll | 131 operationer i 29 delar | Ett 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.
| Adress | Vad |
|---|---|
/.well-known/oauth-protected-resource | Metadata för resursen (RFC 9728) |
/.well-known/oauth-authorization-server | Metadata för auktoriseringsservern (RFC 8414) |
POST /oauth/register | Dynamisk registrering (RFC 7591) |
GET /oauth/authorize | Samtycket, med code_challenge och resource |
POST /oauth/token | Kod 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/metasäger vilken lägsta klientversion servern tar. En äldre klient som skickarX-Client-Versionfår426med kodenclient_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
cursorochlimit, och svaret harnextCursortills 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"
}
| Status | code | Betyder |
|---|---|---|
| 400 | bad_request | Kroppen gick inte att läsa som json, eller ett fält hade fel typ. |
| 401 | unauthenticated | Ingen eller ogiltig token. Tokenen är död: logga in igen. |
| 403 | read_only | Du kan läsa men inte ändra i huset. |
| 403 | forbidden | Rollen räcker inte, till exempel för att ändra medlemmar. |
| 403 | plan_limit | Planen rymmer inte mer. Svaret säger vilket tak. |
| 403 | confirmation_required | Radering av kontot kräver en färsk kod i X-Confirm-Code. |
| 403 | invalid_confirmation | Koden i X-Confirm-Code stämde inte eller har gått ut. |
| 404 | not_found | Finns inte, eller hör till ett hus du inte är med i. Samma svar i båda fallen. |
| 409 | already_member | Personen är redan med i huset. |
| 409 | house_full | Huset har så många medlemmar som planen rymmer. |
| 409 | invite_closed | Inbjudan är redan besvarad, återkallad eller utgången. |
| 409 | owner_fixed | Ägaren går inte att ändra eller ta bort. |
| 409 | active_subscription | Kontot betalar för Plus. Säg upp i kundportalen innan kontot raderas. |
| 409 | owns_shared_house | Kontot äger ett hus som andra är med i. Lämna över det först. |
| 409 | trial_used | Huset har redan provat Plus. |
| 409 | trial_not_needed | Huset har redan Plus. |
| 409 | ai_busy | Ett svar från AI:n pågår redan för huset. |
| 409 | in_progress | Ett anrop med samma Idempotency-Key pågår fortfarande. |
| 409 | replay_unavailable | Svaret till den här Idempotency-Key finns inte kvar att upprepa. |
| 410 | gone | Resursen har gått ut eller ligger i arkivet. |
| 412 | precondition_failed | Någon ändrade resursen efter att du läste den. current har den nya versionen. |
| 413 | payload_too_large | Anropet eller filen är större än taket. |
| 415 | unsupported_media_type | Fel innehållstyp. Läs referensen för vad operationen tar. |
| 422 | validation | Ett eller flera fält är fel. errors har meddelandet per fält. |
| 422 | invalid_code | Inloggningskoden stämde inte. Tokenen och inloggningen påverkas inte. |
| 422 | name_required | Ett nytt konto behöver ett namn. Skicka samma kod igen med name. |
| 403 | signup_closed | Husvo tar inte emot nya konton just nu. Den som har ett konto loggar in som vanligt. |
| 422 | idempotency_key_reuse | Samma Idempotency-Key skickades med ett annat innehåll. |
| 422 | ai_refused | AI:n svarar inte på den frågan. |
| 426 | client_too_old | Appen är äldre än minClientVersion och behöver uppdateras. |
| 428 | precondition_required | Ändringen kräver If-Match med versionen du läste. |
| 429 | rate_limited | För många anrop. Vänta det antal sekunder som Retry-After säger. |
| 429 | quota_exhausted | Husets AI-frågor för månaden är slut. |
| 500 | server_error | Fel hos oss. Skicka med traceId om du hör av dig. |
| 502 | ai_failed | AI-tjänsten svarade inte. Försök igen om en stund. |
| 503 | ai_disabled | AI ä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.
| Vad | Tak | Räknas per |
|---|---|---|
Alla anrop till API:t och /mcp | 300 per minut | Enhet eller anslutning |
| Skrivningar | 60 per minut | Enhet i API:t, användare i MCP |
POST /auth/code | 10 per timme | IP-adress |
POST /auth/token | 20 per timme | IP-adress |
POST /oauth/register | 30 per timme | IP-adress |
POST /oauth/token | 600 per timme | IP-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.