Bifrost exposes a synchronous HTTP API for sending a WhatsApp authentication template message through Turn.
POST /api/v1/otp/send/
Use Django REST Framework token authentication:
Authorization: Token <token>The authenticated Django user must have the synch.send_otp permission.
Send JSON with:
msisdn: required E.164 WhatsApp phone numberotp: required non-empty string up to 15 charactersrecipient_type: required string enum, one ofpatientorsynch_usermetadata: optional object, accepted but ignored in v1
Example:
{
"msisdn": "+27831234567",
"otp": "493821",
"recipient_type": "patient",
"metadata": {
"source": "sync-qa"
}
}recipient_type classifies who the OTP is for. It is request-scoped, does not need to match a local patient record, and does not currently change delivery behavior. synch_user refers to the OTP recipient's role in SyNCH, not the authenticated API caller.
200 OK
{
"status": "submitted",
"message_id": "wamid.123"
}message_id is the provider-assigned identifier returned by Turn when the message is accepted.
400 Bad Request: request validation failed401 Unauthorized: missing or invalid token403 Forbidden: authenticated caller lackssynch.send_otp429 Too Many Requests: per-contact delivery protection throttle502 Bad Gateway: non-timeout upstream Turn failure504 Gateway Timeout: timed out waiting for Turn; delivery outcome is unknown
Retries after 502 or 504 may duplicate delivery because Turn does not provide an idempotency mechanism for this flow.
This API uses these settings:
TURN_BASE_URLTURN_OTP_TOKENTURN_OTP_TEMPLATE_NAMESPACETURN_OTP_TEMPLATE_NAMETURN_OTP_TEMPLATE_LANGUAGEOTP_DELIVERY_THROTTLE_RATEwith default6/husing standard DRF rate syntax
The throttle state is stored in a dedicated Django database cache table created by migrations.
- OpenAPI schema:
/api/schema/ - Swagger UI:
/api/docs/