{"openapi":"3.1.0","info":{"title":"marve-py-otp-service","version":"0.1.0"},"paths":{"/otps/health":{"get":{"tags":["OTPs"],"summary":"Health check","operationId":"health_otps_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"properties":{"message":{"type":"string"}},"additionalProperties":true,"type":"object","required":["message"],"title":"Health"},"example":{"message":"marve-py-otp-service is okay and running, fast!"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"additionalProperties":true,"type":"object","required":["error_reference","error_type","errors","status_code","timestamp"],"title":"ErrorEnvelope"},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}}}}},"/otps/{reference}":{"post":{"tags":["OTPs"],"summary":"Check a code","description":"Checks `otp_code` against the code sent to `otp_contact` for this reference. On success the code is used up and a new reference is returned for the next step. A wrong code, a wrong contact or an expired reference is a 400.","operationId":"validate_otp_otps__reference__post","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"OtpReference","type":"object","properties":{"reference":{"type":"string"}},"required":["reference"],"additionalProperties":true},"example":{"reference":"11111111-1111-1111-1111-111111111111"}}}},"400":{"description":"The body is missing, not JSON, has a field it should not, or a value fails validation","content":{"application/json":{"schema":{"title":"ErrorEnvelope","type":"object","properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"required":["error_reference","error_type","errors","status_code","timestamp"],"additionalProperties":true},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"title":"ErrorEnvelope","type":"object","properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"required":["error_reference","error_type","errors","status_code","timestamp"],"additionalProperties":true},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"title":"ValidateOtpBody","type":"object","properties":{"otp_code":{"type":"string","description":"The 6-digit code that was sent."},"otp_contact":{"type":"string","description":"The email address or phone number it was sent to."}},"required":["otp_code","otp_contact"],"additionalProperties":false},"example":{"otp_code":"123456","otp_contact":"jane@example.com"}}}}},"get":{"tags":["OTPs"],"summary":"Read a stored code","description":"Returns the stored OTP when `otp_contact` in the JSON body matches it. This GET takes a body, which Swagger UI cannot send, so try it with curl or another client.","operationId":"get_otp_otps__reference__get","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"OtpRecord","type":"object","properties":{"reference":{"type":"string"},"otp_code":{"type":["string","null"],"description":"The bcrypt hash of the code."},"otp_raw":{"type":["string","null"],"description":"The code itself."},"otp_channel":{"type":"string"},"otp_contact":{"type":"string"},"created_at_timestamp":{"type":["string","null"]}},"required":["reference","otp_channel","otp_contact"],"additionalProperties":true},"example":{"reference":"11111111-1111-1111-1111-111111111111","otp_code":"<bcrypt hash of the code>","otp_raw":"123456","otp_channel":"email","otp_contact":"jane@example.com","created_at_timestamp":"2026-01-01T00:00:00"}}}},"400":{"description":"The body is missing, not JSON, has a field it should not, or a value fails validation","content":{"application/json":{"schema":{"title":"ErrorEnvelope","type":"object","properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"required":["error_reference","error_type","errors","status_code","timestamp"],"additionalProperties":true},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}},"404":{"description":"No OTP for this reference, or the contact does not match it","content":{"application/json":{"schema":{"title":"ErrorEnvelope","type":"object","properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"required":["error_reference","error_type","errors","status_code","timestamp"],"additionalProperties":true},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"title":"ErrorEnvelope","type":"object","properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"required":["error_reference","error_type","errors","status_code","timestamp"],"additionalProperties":true},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"title":"OtpContactBody","type":"object","properties":{"otp_contact":{"type":"string","description":"The email address the code was sent to."}},"required":["otp_contact"],"additionalProperties":true},"example":{"otp_contact":"jane@example.com"}}}}}},"/otps/resend/{reference}":{"post":{"tags":["OTPs"],"summary":"Send a new code","description":"Generates a fresh code for this reference and sends it to `otp_contact`, which must be an email address. Any earlier code for the reference stops working.","operationId":"resend_otp_otps_resend__reference__post","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string","title":"Reference"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"OtpReference","type":"object","properties":{"reference":{"type":"string"}},"required":["reference"],"additionalProperties":true},"example":{"reference":"11111111-1111-1111-1111-111111111111"}}}},"400":{"description":"The body is missing, not JSON, has a field it should not, or a value fails validation","content":{"application/json":{"schema":{"title":"ErrorEnvelope","type":"object","properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"required":["error_reference","error_type","errors","status_code","timestamp"],"additionalProperties":true},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}},"500":{"description":"Unexpected failure. Quote `error_reference` when reporting it","content":{"application/json":{"schema":{"title":"ErrorEnvelope","type":"object","properties":{"error_reference":{"type":"string"},"error_type":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"status_code":{"type":"integer"},"timestamp":{"type":"string"}},"required":["error_reference","error_type","errors","status_code","timestamp"],"additionalProperties":true},"example":{"error_reference":"b7825cd3-59dc-43a9-9e1f-964676d3cf72","error_type":"VALIDATION_ERROR","errors":{"otp_code":["OTP code must be a 6-digit number"]},"status_code":400,"timestamp":"2026-09-30T10:00:00.000000"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"title":"OtpContactBody","type":"object","properties":{"otp_contact":{"type":"string","description":"The email address the code was sent to."}},"required":["otp_contact"],"additionalProperties":true},"example":{"otp_contact":"jane@example.com"}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}