{"openapi":"3.1.0","info":{"title":"Surveymatic API","version":"1.0.0","description":"Create surveys, collect responses with AI follow-up probing, and read per-response confidence scores. The public respondent endpoints (`/api/public/s/:slug/*`) are intentionally undocumented here — they are unauthenticated by design and belong to the respondent side, not the owner/integrator side. Machine access via scoped API keys ships in issue #203 (PR B); until then every endpoint below requires a human session token."},"servers":[{"url":"/","description":"Deployment origin"}],"paths":{"/api/auth/register":{"post":{"summary":"Register","tags":["Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"description":"Registration details"},"responses":{"200":{"description":"Session token and user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionToken"}}}},"400":{"description":"Email already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/auth/login":{"post":{"summary":"Log in","tags":["Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"description":"Credentials"},"responses":{"200":{"description":"Session token and user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionToken"}}}},"401":{"description":"Invalid email or password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/auth/logout":{"post":{"summary":"Log out","tags":["Auth"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Logged out","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}}},"/api/auth/me":{"get":{"summary":"Current user","tags":["Auth"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Signed-in user","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"}},"required":["user"]}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surveys":{"get":{"summary":"List surveys","tags":["Surveys"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Owned surveys","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Survey"}}}}}}},"post":{"summary":"Create survey","description":"Creates a survey in `draft` status.","tags":["Surveys"],"security":[{"sessionToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSurveyRequest"}}},"description":"Survey details"},"responses":{"201":{"description":"Created survey","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Survey"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surveys/{id}":{"get":{"summary":"Get survey","tags":["Surveys"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}}],"responses":{"200":{"description":"Survey with questions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurveyDetail"}}}},"404":{"description":"Survey not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Save survey","description":"Replaces title, description, status, layout, and questions. Publishing (`status: live`) requires a human session — machine credentials cannot publish (issue #203).","tags":["Surveys"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveSurveyRequest"}}},"description":"Full survey definition"},"responses":{"200":{"description":"Saved survey","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurveyDetail"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Survey not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete survey","description":"Human session only — excluded from scoped API keys in v1.","tags":["Surveys"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"404":{"description":"Survey not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surveys/{id}/duplicate":{"post":{"summary":"Duplicate survey","tags":["Surveys"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}}],"responses":{"200":{"description":"Duplicated survey (draft)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Survey"}}}},"404":{"description":"Survey not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surveys/{id}/close":{"post":{"summary":"Close survey","description":"Stops a live survey; responses are no longer accepted.","tags":["Surveys"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}}],"responses":{"200":{"description":"Closed survey","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Survey"}}}},"404":{"description":"Survey not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surveys/{id}/results":{"get":{"summary":"Get results","description":"Response-level results with per-response confidence scores, integrity flags, and reasons.","tags":["Results"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}}],"responses":{"200":{"description":"Results with confidence scores","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Results"}}}},"404":{"description":"Survey not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surveys/{id}/results.csv":{"get":{"summary":"Export results CSV","tags":["Results"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}}],"responses":{"200":{"description":"CSV export (scores/flags, not answers)","content":{"text/csv":{"schema":{"type":"string"}}}},"404":{"description":"Survey not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/surveys/{id}/responses/{responseId}":{"patch":{"summary":"Include or exclude a response","tags":["Results"],"security":[{"sessionToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Survey id","schema":{"type":"integer"}},{"name":"responseId","in":"path","required":true,"description":"Response id","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetResponseIncludedRequest"}}},"description":"Inclusion flag"},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"404":{"description":"Response not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/team":{"get":{"summary":"Get team","tags":["Team"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Team snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}}}}},"/api/team/invites":{"post":{"summary":"Invite team member","tags":["Team"],"security":[{"sessionToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTeamInviteRequest"}}},"description":"Invite details"},"responses":{"200":{"description":"Invite created","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/team/invites/{inviteId}":{"delete":{"summary":"Revoke invite","tags":["Team"],"security":[{"sessionToken":[]}],"parameters":[{"name":"inviteId","in":"path","required":true,"description":"Invite id","schema":{"type":"string"}}],"responses":{"200":{"description":"Invite revoked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}}},"/api/team/members/{userId}":{"delete":{"summary":"Remove team member","tags":["Team"],"security":[{"sessionToken":[]}],"parameters":[{"name":"userId","in":"path","required":true,"description":"Member user id","schema":{"type":"string"}}],"responses":{"200":{"description":"Member removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}}},"/api/team/accept":{"post":{"summary":"Accept invite","tags":["Team"],"security":[{"sessionToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptTeamInviteRequest"}}},"description":"Invite token"},"responses":{"200":{"description":"Invite accepted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Invalid or expired invite","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/team/leave":{"post":{"summary":"Leave team","tags":["Team"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Left team","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}}},"/api/team/invite-preview":{"get":{"summary":"Preview invite","tags":["Team"],"responses":{"200":{"description":"Invite preview","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}}}}}},"404":{"description":"Invite not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/billing/status":{"get":{"summary":"Billing status","description":"Human session only. Scoped API keys are rejected on billing endpoints (issue #203).","tags":["Billing"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Billing status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingStatus"}}}}}}},"/api/billing/checkout-session":{"post":{"summary":"Create checkout session","description":"Human session only. Scoped API keys are rejected on billing endpoints (issue #203).","tags":["Billing"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Stripe Checkout URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/billing/portal-session":{"post":{"summary":"Create billing portal session","description":"Human session only. Scoped API keys are rejected on billing endpoints (issue #203).","tags":["Billing"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Portal URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}}}}}}}}},"/api/billing/switch-plan":{"post":{"summary":"Switch plan","description":"Human session only. Scoped API keys are rejected on billing endpoints (issue #203).","tags":["Billing"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Updated billing status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingStatus"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/billing/sync":{"post":{"summary":"Sync billing from Stripe","description":"Human session only. Scoped API keys are rejected on billing endpoints (issue #203).","tags":["Billing"],"security":[{"sessionToken":[]}],"responses":{"200":{"description":"Billing status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingStatus"}}}}}}}},"components":{"securitySchemes":{"sessionToken":{"type":"apiKey","in":"header","name":"x-session-token","description":"Browser session token issued at login/register. Scoped API keys (issue #203, PR B) will use `Authorization: Bearer smk_...` instead."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"],"additionalProperties":false,"description":"Error payload"},"User":{"type":"object","properties":{"id":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"firstName":{"type":"string"},"lastName":{"type":"string"},"plan":{"type":"string"},"planStatus":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","email","firstName","lastName","plan"],"additionalProperties":false,"description":"Public user profile returned by auth endpoints"},"StoredQuestion":{"type":"object","properties":{"id":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"surveyId":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"type":{"type":"string","enum":["single","multi","scale","nps","text"]},"prompt":{"type":"string"},"options":{"type":"array","items":{"type":"string"}},"required":{"type":"boolean"},"sortOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","surveyId","type","prompt","options","required","sortOrder"],"additionalProperties":false,"description":"A stored survey question"},"Survey":{"type":"object","properties":{"id":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"ownerId":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"title":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","live","paused","closed"]},"respondLayout":{"type":"string","enum":["one_at_a_time","single_page"]},"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","ownerId","title","description","slug","status","respondLayout","settings","createdAt","updatedAt"],"additionalProperties":false,"description":"A survey. New surveys start as `draft`."},"ResultAnswer":{"type":"object","properties":{"questionId":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"value":{}},"required":["questionId","value"],"additionalProperties":false,"description":"One answer within a response"},"Results":{"type":"object","properties":{"survey":{"type":"object","properties":{"id":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"title":{"type":"string"},"status":{"type":"string"}},"required":["id","title","status"],"additionalProperties":false},"totalResponses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"avgConfidence":{"type":"number"},"higherConfidenceCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"needsReviewCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"responses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"confidenceScore":{"type":"number"},"flags":{"type":"array","items":{"type":"string"}},"reasons":{"type":"array","items":{"type":"string"}},"affirmations":{"type":"array","items":{"type":"string"}},"included":{"type":"boolean"},"durationMs":{"type":"number"},"createdAt":{"type":"string"},"answers":{"type":"array","items":{"type":"object","properties":{"questionId":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"value":{}},"required":["questionId","value"],"additionalProperties":false}}},"required":["id","confidenceScore","flags","reasons","affirmations","included","durationMs","createdAt","answers"],"additionalProperties":false}}},"required":["survey","totalResponses","avgConfidence","higherConfidenceCount","needsReviewCount","responses"],"additionalProperties":false,"description":"Response-level results with per-response confidence scores and integrity flags"},"TeamMember":{"type":"object","properties":{"userId":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"type":"string"},"role":{"type":"string","enum":["owner","member"]},"createdAt":{"type":"string"}},"required":["userId","email","name","role","createdAt"],"additionalProperties":false,"description":"A team member"},"Team":{"type":"object","properties":{"role":{"anyOf":[{"type":"string","enum":["owner","member"]},{"type":"null"}]},"billingOwnerId":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"seatUsed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"seatLimit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"canInvite":{"type":"boolean"},"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"type":"string"},"role":{"type":"string","enum":["owner","member"]},"createdAt":{"type":"string"}},"required":["userId","email","name","role","createdAt"],"additionalProperties":false}}},"required":["role","billingOwnerId","seatUsed","seatLimit","canInvite","members"],"additionalProperties":false,"description":"Team snapshot for the signed-in user"},"BillingStatus":{"type":"object","properties":{"plan":{"type":"string"},"planStatus":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["plan"],"additionalProperties":{},"description":"Billing status. Human session only — scoped API keys are rejected on billing endpoints."},"RegisterRequest":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"password":{"type":"string","minLength":4},"firstName":{"type":"string","minLength":1,"maxLength":80},"lastName":{"type":"string","minLength":1,"maxLength":80}},"required":["email","password","firstName","lastName"],"additionalProperties":false,"description":"Registration details"},"LoginRequest":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"password":{"type":"string","minLength":4}},"required":["email","password"],"additionalProperties":false,"description":"Login credentials"},"CreateSurveyRequest":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":2000},"respondLayout":{"type":"string","enum":["one_at_a_time","single_page"]}},"required":["title"],"additionalProperties":false,"description":"Survey details"},"SaveSurveyRequest":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":2000},"status":{"type":"string","enum":["draft","live","paused","closed"]},"respondLayout":{"type":"string","enum":["one_at_a_time","single_page"]},"questions":{"minItems":1,"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"clientKey":{"type":"string","minLength":1,"maxLength":64},"type":{"type":"string","enum":["single","multi","scale","nps","text"]},"prompt":{"type":"string","minLength":1},"options":{"default":[],"type":"array","items":{"type":"string"}},"required":{"default":true,"type":"boolean"},"sortOrder":{"default":0,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"skipRule":{"anyOf":[{"type":"object","properties":{"ifOption":{"type":"string","minLength":1,"maxLength":200},"skipToKey":{"type":"string","minLength":1,"maxLength":64}},"required":["ifOption","skipToKey"],"additionalProperties":false},{"type":"null"}]}},"required":["type","prompt","options","required","sortOrder"],"additionalProperties":false}}},"required":["title","questions"],"additionalProperties":false,"description":"Full survey definition"},"QuestionInput":{"type":"object","properties":{"id":{"type":"number"},"clientKey":{"type":"string","minLength":1,"maxLength":64},"type":{"type":"string","enum":["single","multi","scale","nps","text"]},"prompt":{"type":"string","minLength":1},"options":{"default":[],"type":"array","items":{"type":"string"}},"required":{"default":true,"type":"boolean"},"sortOrder":{"default":0,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"skipRule":{"anyOf":[{"type":"object","properties":{"ifOption":{"type":"string","minLength":1,"maxLength":200},"skipToKey":{"type":"string","minLength":1,"maxLength":64}},"required":["ifOption","skipToKey"],"additionalProperties":false},{"type":"null"}]}},"required":["type","prompt","options","required","sortOrder"],"additionalProperties":false,"description":"A question in a save request"},"SetResponseIncludedRequest":{"type":"object","properties":{"included":{"type":"boolean"}},"required":["included"],"additionalProperties":false,"description":"Include/exclude flag for a response"},"CreateTeamInviteRequest":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}},"required":["email"],"additionalProperties":false,"description":"Invite details"},"AcceptTeamInviteRequest":{"type":"object","properties":{"token":{"type":"string","minLength":1}},"required":["token"],"additionalProperties":false,"description":"Invite token"},"SessionToken":{"type":"object","properties":{"token":{"type":"string"},"user":{"type":"object","properties":{},"additionalProperties":{}}},"required":["token","user"],"additionalProperties":false,"description":"Session token and user"},"SurveyDetail":{"description":"A survey with its questions","allOf":[{"$ref":"#/components/schemas/Survey"},{"type":"object","properties":{"questions":{"type":"array","items":{"$ref":"#/components/schemas/StoredQuestion"}}}}]}}}}