Auth (Reference)
For most users the login + refresh pair in Getting Started → Authentication is all you need. This page documents the two endpoints that aren’t strictly necessary for a daily-loop client but are required for programmatic onboarding (signup) and user-profile management (me).
POST /auth/signup
Section titled “POST /auth/signup”Create a new account via Supabase Auth. Optional profile fields are stored on the Supabase user’s user_metadata and seeded into public.users on the first authenticated request.
Auth: None
Example
Section titled “Example”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 }'Request body
Section titled “Request body”{ "email": "user@example.com", "password": "securepassword", "location_zip": "92101", "home_size_sqft": 2000, "pricing_location": 1}| Field | Type | Required | Notes |
|---|---|---|---|
email | string | Yes | Must be unique |
password | string | Yes | Subject to the project’s Supabase password policy |
location_zip | string | No | US zip code, used for weather forecasts. Stored on user_metadata.location_zip. |
home_size_sqft | integer | No | Used to estimate HVAC power draw. Stored on user_metadata.home_size_sqft. |
pricing_location | integer | No | TOU pricing zone 1–8 (default: 1). Stored on user_metadata.pricing_location. |
Response (200) — session returned
Section titled “Response (200) — session returned”When the project does not require email confirmation, the response is a full Supabase 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 the project requires email confirmation, the session fields (access_token, refresh_token, expires_in, expires_at, token_type) may be absent and the body contains only the pending user row:
{ "user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com" }}Clients must handle both shapes — typically by treating the absence of access_token as “show a confirm-your-email screen.”
Errors
Section titled “Errors”| Status | Detail | Cause |
|---|---|---|
| 400 | "User already registered" | Email is already in use (or other Supabase signup failure message) |
| 422 | "Password should be at least 6 characters" | Supabase password policy rejection |
| 502 | "Auth service unreachable" | Supabase upstream failure |
| 503 | "Auth not configured" | SUPABASE_URL / SUPABASE_PUBLISHABLE_KEY missing on the API |
GET /auth/me
Section titled “GET /auth/me”Get the current user’s profile and subscription tier. Lazily creates the user’s public.users row keyed to auth.users(id) on first call, seeded from the JWT’s user_metadata claim.
Auth: Required (Bearer JWT)
Example
Section titled “Example”curl https://api.hungrymachines.io/auth/me \ -H "Authorization: Bearer YOUR_TOKEN"Response (200)
Section titled “Response (200)”{ "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"}Response fields
Section titled “Response fields”| Field | Type | Notes |
|---|---|---|
user_id | string (UUID) | Matches the Supabase auth.users.id (the JWT sub claim) |
email | string | |
location_zip | string | May be empty |
home_size_sqft | integer | May be 0 |
pricing_location | integer | TOU pricing zone 1–8 |
timezone | string | IANA timezone |
subscription_tier | string | "free" or "premium". Defaults to "free" if no active subscription. |
weather_entity_id | string | The Home Assistant weather.* entity the user picked in the panel’s Settings tab. Empty string when unset — the API then falls back to Open-Meteo for nightly forecasts. |
Errors
Section titled “Errors”| Status | Detail | Cause |
|---|---|---|
| 401 | "Not authenticated" / "Invalid token" / "Token expired" | Missing, malformed, or expired token |
| 503 | "Database unavailable" / "Auth not configured" | Backend dependency unavailable |
PATCH /auth/me
Section titled “PATCH /auth/me”Update the user’s profile row. Send only the fields you want to change.
Auth: Required (Bearer JWT)
Example
Section titled “Example”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" }'Request body
Section titled “Request body”All fields are optional — include only what you want to change. Sending null clears the value.
{ "location_zip": "94110", "home_size_sqft": 1800, "pricing_location": 3, "timezone": "America/Los_Angeles", "weather_entity_id": "weather.home"}| Field | Type | Validation |
|---|---|---|
location_zip | string | null | US zip code |
home_size_sqft | integer | null | |
pricing_location | integer | null | 1–8 |
timezone | string | null | IANA timezone |
weather_entity_id | string | null | HA weather.* entity id. The nightly job’s forecast resolver prefers a pushed forecast from this entity over the Open-Meteo fallback. |
Response (200)
Section titled “Response (200)”Returns the full profile object after the update — same shape as GET /auth/me.
Errors
Section titled “Errors”| Status | Detail | Cause |
|---|---|---|
| 401 | "Not authenticated" | Missing or invalid token |
| 503 | "Database unavailable" | Database not configured or write failed |