{"openapi":"3.0.3","info":{"title":"H2Home API","version":"1.0.0","description":"H2Home is a premium bottled water delivery subscription service in Atlanta, GA. This spec covers the public API surface. Customer endpoints authenticate with a better-auth session cookie obtained by signing in at /signin. All API responses include RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, and RateLimit-Policy headers; 429 responses include Retry-After. Human-readable docs: https://www.h2home.io/docs. Agent guidance: https://www.h2home.io/llms.txt.","contact":{"name":"H2Home Support","url":"https://www.h2home.io/contact"}},"servers":[{"url":"https://www.h2home.io","description":"Production"}],"tags":[{"name":"contact","description":"Contact the H2Home team"},{"name":"analytics","description":"Onboarding funnel analytics"},{"name":"account","description":"Authenticated customer account"}],"paths":{"/api/contact":{"post":{"operationId":"submitContactForm","tags":["contact"],"summary":"Submit a contact form message","description":"Sends a message to the H2Home team by email. Public — no authentication required. Rate limited to 5 requests per minute per IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}}},"responses":{"200":{"description":"Message accepted and forwarded to the team.","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"Quota policy, e.g. \"5;w=60\" (5 requests per 60 seconds).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"429":{"description":"Rate limit exceeded. Wait Retry-After seconds before retrying.","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"Quota policy, e.g. \"5;w=60\" (5 requests per 60 seconds).","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The message could not be sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}}}}},"/api/funnel":{"post":{"operationId":"recordFunnelEvent","tags":["analytics"],"summary":"Record an onboarding funnel event","description":"Records a signup/onboarding analytics event. Public — works for pre-signup visitors; events from signed-in users are associated with their account. Rate limited to 60 requests per minute per IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunnelEvent"}}}},"responses":{"200":{"description":"Event recorded.","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"Quota policy, e.g. \"5;w=60\" (5 requests per 60 seconds).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"400":{"description":"Missing required step field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Wait Retry-After seconds before retrying.","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"Quota policy, e.g. \"5;w=60\" (5 requests per 60 seconds).","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/me":{"get":{"operationId":"getCurrentUser","tags":["account"],"summary":"Get the current authenticated user","description":"Returns a status message for the signed-in customer. Requires a better-auth session cookie; unauthenticated requests receive 401.","security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Authenticated request succeeded.","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"Quota policy, e.g. \"5;w=60\" (5 requests per 60 seconds).","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"401":{"description":"No valid session cookie was provided.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. Wait Retry-After seconds before retrying.","headers":{"RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"Quota policy, e.g. \"5;w=60\" (5 requests per 60 seconds).","schema":{"type":"string"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"sessionCookie":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Session cookie issued by better-auth after signing in at /signin (email/password or Google OAuth)."}},"schemas":{"ContactRequest":{"type":"object","required":["name","email","message"],"properties":{"name":{"type":"string","description":"Sender's full name.","example":"Ada Lovelace"},"email":{"type":"string","format":"email","description":"Sender's email address for the reply.","example":"ada@example.com"},"subject":{"type":"string","description":"Short subject line.","example":"Delivery zones"},"message":{"type":"string","description":"Message body.","example":"Do you deliver to Decatur, GA?"}}},"FunnelEvent":{"type":"object","required":["step"],"properties":{"step":{"type":"string","description":"Funnel step identifier.","example":"select_water_brand"},"stepNumber":{"type":"integer","description":"Ordinal position of the step in the funnel."},"stepName":{"type":"string","description":"Human-readable step name."},"completed":{"type":"boolean","description":"Whether the step was completed.","default":false},"abandoned":{"type":"boolean","description":"Whether the visitor abandoned at this step.","default":false},"email":{"type":"string","format":"email","description":"Visitor email, for pre-signup events with no session."},"sessionId":{"type":"string","description":"Client-generated session identifier."},"source":{"type":"string","description":"Traffic source (UTM source)."},"medium":{"type":"string","description":"Traffic medium (UTM medium)."},"campaign":{"type":"string","description":"Campaign name (UTM campaign)."},"device":{"type":"string","description":"Device category."},"browser":{"type":"string","description":"Browser name."},"duration":{"type":"integer","description":"Time spent on the step, in milliseconds."},"data":{"type":"object","additionalProperties":true,"description":"Arbitrary extra event properties."}}},"Message":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable status message."}}},"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message."}}}}}}