Skip to content

Auth (Reference)

For most clients the login + refresh pair in Getting Started → Authentication is all you need. This page documents the endpoints that aren’t strictly part of the daily loop but are useful for programmatic onboarding (signup) and profile management (me).


Create a new account. Optional profile fields are stored on the account and applied the first time the client calls GET /auth/me, so call GET /auth/me once after signup or login, before any other request.

Auth: None

Only email and password are required. Every profile field below is optional and can be set (or changed) later via PATCH /auth/me.

Terminal window
curl -X POST https://api.hungrymachines.io/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securepassword",
"location_zip": "92101",
"home_size_sqft": 2000,
"pricing_location": 1,
"timezone": "America/Los_Angeles"
}'
Field Type Required Notes
email string Yes Must be unique
password string Yes Subject to the account password policy
location_zip string No US zip code, used for weather forecasts
home_size_sqft integer No Used to estimate appliance power draw
pricing_location integer No Rate-catalog id (see available_pricing_zones from GET /api/v1/rates; default 1)
timezone string No IANA timezone name, for example America/Los_Angeles. 422 if it isn’t a real zone.
newsletter_opt_in boolean No Newsletter checkbox state. Omitted means opted in.

Send timezone at signup so the account starts on the right local day — every schedule is keyed to the account’s local midnight. An account created without one starts on America/New_York until PATCH /auth/me sets it.

When the account doesn’t require email confirmation, the response is a full session:

{
"access_token": "eyJhbGciOi...",
"refresh_token": "JKLrM3...",
"expires_in": 3600,
"expires_at": 1762450800,
"token_type": "bearer",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com"
}
}

Response (200) — email confirmation pending

Section titled “Response (200) — email confirmation pending”

When email confirmation is required, the response is still 200 but the session fields (access_token, refresh_token, expires_in, expires_at, token_type) are absent. For example:

{
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com"
}
}

Clients must handle both shapes: treat a 200 without access_token as “show a confirm-your-email screen”, and do not depend on any other key in that body.

Status Detail Cause
400 "User already registered" Email is already in use (or another signup failure message)
422 "Password should be at least 6 characters" Password policy rejection (string detail)
422 "timezone must be a valid IANA zone name, got '...'" timezone isn’t a real IANA zone
422 array detail email or password missing or not a string. See Errors, Limits & Stability.
502 "Auth service unreachable" Identity provider unreachable or failing
503 "Auth not configured" Auth not configured on the API

The 400 and the password-policy 422 come from the identity provider and are passed through with its status code and message. Treat the codes above as the common cases, not a closed list: handle any other 4xx on this route as a failed signup and show its detail.


Get the current user’s profile and subscription tier. Call it once right after signup or login, before any other request: the first call sets up the account’s profile and applies the optional fields sent at signup.

Auth: Required (Bearer JWT)

Terminal window
curl https://api.hungrymachines.io/auth/me \
-H "Authorization: Bearer YOUR_TOKEN"
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"location_zip": "92101",
"home_size_sqft": 2000,
"pricing_location": 1,
"timezone": "America/New_York",
"subscription_tier": "free",
"weather_entity_id": "weather.home"
}
Field Type Notes
user_id string (UUID) Account id (matches the JWT subject)
email string
location_zip string May be empty
home_size_sqft integer May be 0
pricing_location integer Rate-catalog id (see available_pricing_zones)
timezone string IANA timezone
subscription_tier string "free" or "premium". Defaults to "free".
weather_entity_id string The Home Assistant weather.* entity the user picked for forecasts. Empty when unset (a fallback weather source is then used).
Status Detail Cause
401 "Not authenticated" / "Invalid token" / "Token expired" / "Token missing subject" Missing, malformed, or expired token
503 "Database unavailable" / "Auth not configured" Backend dependency unavailable

These 401 responses are the same on every route that requires auth.


Update the profile. Send only the fields you want to change.

Auth: Required (Bearer JWT)

All fields are optional — include only what you want to change. An omitted field, or one sent as null, is left unchanged; null does not clear a value. To clear location_zip or weather_entity_id, send "".

Terminal window
curl -X PATCH https://api.hungrymachines.io/auth/me \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"location_zip": "94110",
"home_size_sqft": 1800,
"pricing_location": 3,
"timezone": "America/Los_Angeles"
}'
{
"location_zip": "94110",
"home_size_sqft": 1800,
"pricing_location": 3,
"timezone": "America/Los_Angeles",
"weather_entity_id": "weather.home"
}
Field Type Required Notes
location_zip string No US zip code. Send "" to clear. Not checked as a real zip.
home_size_sqft integer No
pricing_location integer No A rate-catalog id from available_pricing_zones (GET /api/v1/rates). 422 if it isn’t one.
timezone string No IANA timezone name, for example America/Los_Angeles. Schedules are keyed to it. 422 if it isn’t a real zone.
weather_entity_id string No Home Assistant weather.* entity id. Send "" to clear.

Returns the full profile object after the update — same shape as GET /auth/me.

Status Detail Cause
401 as on GET /auth/me Missing, invalid or expired token
422 array detail A field has the wrong type
422 "timezone must be a valid IANA zone name, got '...'" timezone isn’t a real IANA zone
422 "pricing_location must be one of the catalog ids: [...]" pricing_location isn’t a known catalog id
503 "Database unavailable" Database not reachable