Overview
The Hawk Send API lets you send WhatsApp messages, deliver OTP verification codes, and manage contacts programmatically. All endpoints are RESTful and return JSON.
Base URLhttps://api.hawksend.com/api/v1
Authentication
All API requests require authentication via a Bearer token. Use your API key (starts with wsa_) in the Authorization header.
Authorization: Bearer wsa_your_api_key_hereGenerate API keys from the Dashboard → API Keys section. Keys are shown once on creation — store them securely.
Login
/auth/login/— Get JWT tokenscurl -X POST https://api.hawksend.com/api/v1/auth/login/ \
-H "Content-Type: application/json" \
-d '{"email": "john@company.com", "password": "securepassword"}'OTP Verification
Send and verify one-time passwords via WhatsApp. OTP codes are hashed before storage and automatically expire.
Send OTP
/otp/send/— Send a verification codecurl -X POST https://api.hawksend.com/api/v1/otp/send/ \
-H "Authorization: Bearer wsa_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"to": "+2348012345678",
"code_length": 6,
"expiry": 300,
"waba_id": "a1b2c3d4-5678-..."
}'{
"otp_id": "a3f8e4b2-9c1d-4e5f...",
"to": "+2348012345678",
"expires_at": "2026-04-05T10:05:00Z",
"status": "pending"
}code_length — 4 to 8 digits (default: 6)
expiry — 60 to 600 seconds (default: 300)
waba_id — Optional. UUID of the connected WhatsApp account (phone number) to send from. If you have multiple phone numbers connected, use this to choose which one sends the OTP. Find the ID in your Dashboard → WhatsApp Integration. Defaults to your primary number if omitted.
Verify OTP
/otp/verify/— Verify a codecurl -X POST https://api.hawksend.com/api/v1/otp/verify/ \
-H "Authorization: Bearer wsa_your_api_key" \
-H "Content-Type: application/json" \
-d '{"to": "+2348012345678", "code": "845291"}'{
"verified": true,
"to": "+2348012345678",
"verified_at": "2026-04-05T10:02:30Z"
}Messages
Send WhatsApp messages in multiple formats. All messages are queued and processed asynchronously.
Send Message
/messages/— Send a WhatsApp messageto — Recipient phone number in international format (e.g. +2348012345678)
type — Message type: text, template, image, document, video, audio
waba_id — Optional. UUID of the connected WhatsApp account (phone number) to send from. If you have multiple phone numbers connected, use this to choose which one sends the message. Find the ID in your Dashboard → WhatsApp Integration. Defaults to your primary number if omitted.
Text Message
curl -X POST https://api.hawksend.com/api/v1/messages/ \
-H "Authorization: Bearer wsa_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"to": "+2348012345678",
"type": "text",
"body": "Hello from Hawk Send!",
"waba_id": "a1b2c3d4-5678-..."
}'Template Message
curl -X POST https://api.hawksend.com/api/v1/messages/ \
-H "Authorization: Bearer wsa_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"to": "+2348012345678",
"type": "template",
"template_name": "hello_world",
"language": "en_US",
"waba_id": "a1b2c3d4-5678-..."
}'Image Message
{
"to": "+2348012345678",
"type": "image",
"url": "https://example.com/photo.jpg",
"caption": "Check this out",
"waba_id": "a1b2c3d4-5678-..."
}Document Message
{
"to": "+2348012345678",
"type": "document",
"url": "https://example.com/invoice.pdf",
"filename": "invoice.pdf",
"waba_id": "a1b2c3d4-5678-..."
}List Messages
/messages/list/— List all messages with paginationcurl https://api.hawksend.com/api/v1/messages/list/?status=delivered&page=1 \
-H "Authorization: Bearer wsa_your_api_key"Filters: status, direction, message_type, page
Statuses: queued, sent, delivered, read, failed
Directions: outbound, inbound
Types: text, template, image, document, otp
Contacts
/messages/contacts/— List contacts/messages/contacts/— Create a contact/messages/contacts/{id}/— Update a contact/messages/contacts/{id}/— Delete a contact/messages/contacts/import/— Import contacts from CSV/messages/contacts/bulk-delete/— Bulk delete contacts/messages/contacts/segments/— List segments with countscurl -X POST https://api.hawksend.com/api/v1/messages/contacts/ \
-H "Authorization: Bearer wsa_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Amina",
"last_name": "Kofi",
"phone": "+233554239901",
"tags": ["VIP", "Lagos"],
"segment": "Premium"
}'Campaigns
Send bulk messages to a contact segment or a list of phone numbers.
/messages/campaigns/— List campaigns/messages/campaigns/— Create and send a campaign/messages/campaigns/{id}/— Campaign detailscurl -X POST https://api.hawksend.com/api/v1/messages/campaigns/ \
-H "Authorization: Bearer wsa_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Q1 Promo",
"segment": "Premium",
"message_type": "template",
"template_name": "promo_q1_2026",
"template_language": "en_US",
"waba_id": "a1b2c3d4-5678-..."
}'segment — Send to all contacts in this segment. Alternatively, provide a phones array.
waba_id — Optional. UUID of the connected WhatsApp account to send from. Defaults to your primary number if omitted.
Webhooks
Hawk Send delivers real-time events to your webhook URL via POST requests. Each WhatsApp account has its own webhook URL and signing secret.
Event Types
message.status — Delivery status updates (sent, delivered, read, failed)
message.received — Inbound messages from customers
otp.verified — OTP code verified successfully
Payload Example
{
"event": "message.status",
"data": {
"message_id": "a3f8e4b2-9c1d-4e5f...",
"status": "delivered",
"to": "+2348012345678",
"timestamp": "2026-04-05T09:30:00Z"
},
"event_id": "evt_...",
"timestamp": "2026-04-05T09:30:01Z"
}Signature Verification
Verify the X-HawkSend-Signature header by computing the HMAC SHA-256 of the raw body using your webhook secret.
import hmac, hashlib
def verify(payload_bytes, signature, secret):
expected = hmac.new(
secret.encode(), payload_bytes, hashlib.sha256
).hexdigest()
return hmac.compare_digest(
f"sha256={expected}", signature
)Billing
/billing/usage/— Get current month usage/billing/checkout/— Create checkout sessioncurl https://api.hawksend.com/api/v1/billing/usage/ \
-H "Authorization: Bearer wsa_your_api_key"Errors
The API returns standard HTTP status codes. Error responses include a JSON body with an error field.
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 202 | Accepted (queued for processing) |
| 204 | No content (successful delete) |
| 400 | Bad request — invalid parameters |
| 401 | Unauthorized — invalid or missing API key |
| 403 | Forbidden — email not verified or plan limit reached |
| 404 | Not found |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
Rate Limits
API requests are rate-limited per tenant based on your plan.
| Plan | Requests/min | Credits/mo | WhatsApp Accounts |
|---|---|---|---|
| Free | 10 | $0 | 0 |
| Starter ($29) | 60 | $14 | 1 |
| Growth ($99) | 500 | $60 | 5 |
| Enterprise ($299) | 1,000 | $200 | Unlimited |
OTP requests are additionally limited to 5 per phone number per hour. When rate limited, the API returns 429 Too Many Requests.
