Send OTP
POST
/api/v1/users/auth/send-otpGenerates a 6-digit one-time passcode for a patient and delivers it over Email or SMS. Returns a generic success response whether or not the user exists, by design (no user enumeration).
cv-api-key
Production
https://api.care360-next.carevalidate.com/api/v1/users/auth/send-otpStaging
https://api-staging.care360-next.carevalidate.com/api/v1/users/auth/send-otpnote
Provide exactly one of email or phoneNumber, and a channel that matches that identifier (SMS ⇒ phoneNumber, EMAIL ⇒ email).
Headers
Headers
cv-api-keystringrequiredYour unique API key for authentication.
Content-TypestringrequiredMust be application/json.
Request Body
Body
emailstringoptionalPatient's email address. Lower-cased and trimmed by the server. Required if phoneNumber is not provided.
Example:
patient@example.comphoneNumberstringoptionalE.164-formatted phone number. Required if email is not provided.
Example:
+15551234567channelstringrequiredDelivery channel for the code. Case-sensitive.
Values:SMSEMAIL
note
Cross-field rules: exactly one of email or phoneNumber must be provided. channel: "SMS" requires phoneNumber. channel: "EMAIL" requires email.
Behavior
- Resolves the organization from
cv-api-key. - Looks up the user scoped to the organization by email or phone.
- If no user is found, returns the generic success response without sending anything (no user enumeration).
- Otherwise generates a 6-digit numeric OTP, hashes it with SHA3-512 → base64, persists it with
expiresAt = now + 5 minutes, and sends it over the chosen channel. - Notification failures are logged but never surface to the caller.
Example Request
- cURL (Email)
- cURL (SMS)
- JavaScript
- Python
curl -X POST '<BASE_URL>/api/v1/users/auth/send-otp' \
-H 'cv-api-key: <redacted>' \
-H 'Content-Type: application/json' \
-d '{
"email": "patient@example.com",
"channel": "EMAIL"
}'
curl -X POST '<BASE_URL>/api/v1/users/auth/send-otp' \
-H 'cv-api-key: <redacted>' \
-H 'Content-Type: application/json' \
-d '{
"phoneNumber": "+15551234567",
"channel": "SMS"
}'
const response = await fetch(
'<BASE_URL>/api/v1/users/auth/send-otp',
{
method: 'POST',
headers: {
'cv-api-key': '<redacted>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'patient@example.com',
channel: 'EMAIL',
}),
}
);
const data = await response.json();
console.log(data);
import requests
response = requests.post(
'<BASE_URL>/api/v1/users/auth/send-otp',
headers={
'cv-api-key': '<redacted>',
'Content-Type': 'application/json',
},
json={
'email': 'patient@example.com',
'channel': 'EMAIL',
},
)
print(response.json())
Responses
▶200Success (generic)Returned whether or not the user exists.
{
"status": 200,
"success": true,
"message": "If an account exists, a verification code has been sent"
}
▶400Validation errorcv-api-key header missing, body fails Zod validation, both/neither identifier provided, or channel/identifier pair mismatched.
{
"status": 400,
"success": false,
"error": "Validation failed",
"code": "VALIDATION_ERROR"
}
▶404Organization not foundcv-api-key does not resolve to a partner organization.
{
"status": 404,
"success": false,
"error": "Organization not found",
"code": "NOT_FOUND"
}
Try It Out
Try itAPI Playground
▶