HelloSend Public API v3 — Reference
Version: v3
Production base URL: https://api.hellosend.com
Table of Contents
- Overview
- Authentication
- Request & Response Conventions
- Errors
- Idempotency
- Endpoints - Auth - Messages - Numbers - Templates - Contacts - Webhooks (REST Hooks) - Webhook Samples
- Webhook Events
- Zapier Integration Mapping
- Getting an API Key
- Endpoint Index
Overview
Path Prefix
All routes use the prefix /api/v3/....
Example: GET https://api.hellosend.com/api/v3/auth/verify
Content Type
- Request bodies:
Content-Type: application/json - Responses:
Content-Type: application/json - Success property names: camelCase (e.g.
serviceId,fromNumber) - Error codes: snake_case (e.g.
invalid_api_key)
Authentication
Every /api/v3 request requires a long-lived API key issued from the HelloSend web app.
| Header | Required | Description |
|---|---|---|
X-HelloSend-API-Key |
Yes | Your API key secret |
Accept |
Recommended | application/json |
Content-Type |
For POST bodies | application/json |
API keys are user-scoped for hooks and sends, and account-scoped for shared resources such as contacts. Revoking a key or deactivating its owner invalidates the key and removes all hook subscriptions created with that key.
Request & Response Conventions
List Responses
List endpoints wrap results in a named array:
| Endpoint Family | Response Key | Item Type |
|---|---|---|
| Numbers | numbers |
Number object |
| Templates | templates |
Template object |
| Contacts | contacts |
Contact object |
| Hooks | hooks |
Hook object |
| Webhook Samples | events |
Webhook envelope |
Dates & Times
| Field | Format | Notes |
|---|---|---|
occurredAt (webhooks) |
ISO 8601 UTC | e.g. 2026-04-20T12:34:56.789Z |
createdTime (hooks) |
ISO 8601 | |
receivedAt, loggedAt (webhook data) |
Unix milliseconds | Integer epoch ms |
Phone Numbers
Use E.164 format (e.g. +14155551234).
Errors
All error responses use this envelope:
{
"error": "snake_case_code",
"message": "Human-readable explanation."
}
HTTP Status Codes
| Status | Typical error Codes |
Meaning |
|---|---|---|
| 400 | invalid_request, missing_field, unsupported_event, invalid_target_url |
Malformed or invalid input |
| 401 | invalid_api_key, authentication_failed |
Missing, revoked, or invalid key |
| 404 | not_found, template_not_found, hook_not_found |
Resource not found |
| 409 | idempotency_in_progress |
Duplicate in-flight request |
| 422 | send_failed, body_not_allowed_for_structured_template |
Business rule violation |
| 429 | — | Rate limited; retry after Retry-After header (planned) |
| 500 | internal_error |
Server error |
Error Code Reference
| Code | Description |
|---|---|
authentication_failed |
Auth context missing |
invalid_api_key |
Key missing, unknown, or revoked |
missing_header |
Required header absent |
invalid_request |
Body or parameters invalid |
missing_field |
Required JSON field absent |
missing_parameter |
Required query parameter absent |
invalid_parameter |
Query parameter out of range or wrong type |
template_not_found |
No template found for given id in this account |
body_not_allowed_for_structured_template |
body sent with a WhatsApp/RCS template |
send_failed |
Provider rejected the outbound message |
unsupported_event |
Hook event not in supported set |
invalid_target_url |
Hook targetUrl must be absolute https:// |
hook_not_found |
Hook id not found for this user |
not_found |
Generic 404 |
internal_error |
Unexpected server failure |
idempotency_in_progress |
Same Idempotency-Key still processing |
Idempotency
POST /api/v3/messages supports optional idempotent sends via the Idempotency-Key request header. Using this header prevents duplicate sends on retries.
| Header | Required | Description |
|---|---|---|
Idempotency-Key |
No (recommended) | A unique string per logical send (UUID recommended) |
Behavior
| Scenario | Result |
|---|---|
| No header | Normal send; no replay cache |
| First request with key | Executes send; caches 2xx response body |
| Same key while first in flight | 409 idempotency_in_progress, Retry-After: 2 |
| Same key after success | Replays cached 2xx body; includes X-Idempotent-Replay: true |
| Same key after non-2xx | Claim released; caller may retry with same key |
Idempotency is scoped to account + user.
Endpoints
Auth
Verify Connection
GET /api/v3/auth/verify
Confirms a valid API key and returns metadata for connection labels.
Response 200
| Field | Type | Description |
|---|---|---|
userEmail |
string | Email of the key owner |
userName |
string | Display name of the key owner |
osyncUserId |
string | Internal user identifier |
accountName |
string | Workspace / account label |
apiKeyId |
string | Key record id |
apiKeyName |
string | Label given at key creation |
Example request
GET /api/v3/auth/verify HTTP/1.1
Host: api.hellosend.com
X-HelloSend-API-Key: hs_live_xxxxxxxxxxxxxxxx
Accept: application/json
Example response
{
"userEmail": "alex@example.com",
"userName": "Alex Morgan",
"osyncUserId": "usr_abc123",
"accountName": "Acme Corp",
"apiKeyId": "key_xyz789",
"apiKeyName": "Zapier production"
}
Messages
Send Message
POST /api/v3/messages
Send an outbound SMS, MMS, WhatsApp, or RCS message.
Request body
| Field | Required | Description |
|---|---|---|
uniqueId |
Yes | Sender id from Numbers list id (senderUniqueId) |
toNumber |
Yes | E.164 recipient number |
body |
Conditional | Message text. Required when no templateId. Omit for structured WhatsApp/RCS templates. |
templateId |
No | HelloSend template id |
variables |
No | Map of placeholder name → string value for template merge |
module |
No | Optional CRM module name for analytics |
Template rules
- SMS/Application template — If
bodyis omitted, the server renders from template content +variables. Ifbodyis provided, it is sent as-is and tagged withtemplateId. - WhatsApp/RCS (structured) template — Rendered from
variablesonly. Sending a non-emptybodyreturns422 body_not_allowed_for_structured_template.
Response 201
| Field | Type | Description |
|---|---|---|
id |
string | Message id |
messageId |
string | Provider message identifier |
status |
string | "sent" on success |
fromNumber |
string | Resolved E.164 sender |
toNumber |
string | Recipient |
serviceId |
string | Resolved integration id |
templateId |
string | Present when a template was used |
module |
string | Present when supplied |
Example — plain SMS
POST /api/v3/messages HTTP/1.1
Host: api.hellosend.com
X-HelloSend-API-Key: hs_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
"uniqueId": "SN_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"toNumber": "+14155551234",
"body": "Your appointment is confirmed for tomorrow at 2pm."
}
Response
{
"id": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"messageId": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "sent",
"fromNumber": "+14155559876",
"toNumber": "+14155551234",
"serviceId": "integ_abc123"
}
Example — template with variables
{
"uniqueId": "SN_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"toNumber": "+14155551234",
"templateId": "tmpl_welcome_001",
"variables": {
"firstName": "Ada",
"orderId": "ORD-42"
}
}
Numbers
List Numbers
GET /api/v3/numbers
Returns phone numbers the authenticated user may send from. Used to populate From Number dropdowns.
Response 200 — numbers[] item
| Field | Type | Description |
|---|---|---|
id |
string | senderUniqueId — pass as uniqueId on send |
integId |
string | Integration id (display; resolved on send) |
phoneNumber |
string | E.164 number (display) |
friendlyName |
string | User-assigned label |
provider |
string | Messaging provider display name |
crm |
string | Connected CRM display name |
channels |
string[] | e.g. ["sms","mms","whatsapp"] |
whatsappBusinessAccountId |
string | Present for WhatsApp-enabled numbers |
label |
string | Pre-rendered dropdown label |
Example response
{
"numbers": [
{
"id": "SN_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"integId": "integ_abc123",
"phoneNumber": "+14155559876",
"friendlyName": "Main sales line",
"provider": "Twilio",
"crm": "HubSpot",
"channels": ["sms", "mms"],
"label": "+14155559876 — HubSpot via Twilio (SMS · MMS)"
}
]
}
Templates
List Templates
GET /api/v3/templates
Query parameters
| Param | Required | Description |
|---|---|---|
integId |
No | Filter to templates for this integration |
module |
No | CRM module name filter |
channels |
No | Comma-separated types: application, whatsapp, twiliorcs. Default: application |
Response 200 — templates[] item
| Field | Type | Description |
|---|---|---|
id |
string | Template id |
name |
string | Friendly name |
moduleName |
string | Module scope (general when global) |
templateType |
string | application, whatsapp, or twiliorcs |
content |
string | Template body (application templates) |
integId |
string | Integration scope when set |
label |
string | Pre-rendered dropdown label |
placeholders |
array | { name, type?, required? } entries |
Get Template
GET /api/v3/templates/{id}
Returns full template detail including placeholders and, for structured templates, components.
Errors: 404 template_not_found when the id does not exist in the account.
Contacts
Find Contacts by Phone
GET /api/v3/contacts?phone={phone}
Account-scoped contact lookup. Returns an empty contacts array when no match — not an error.
Query parameters
| Param | Required | Description |
|---|---|---|
phone |
Yes | Phone number to search (E.164 recommended) |
Response 200 — contacts[] item
| Field | Type | Description |
|---|---|---|
id |
string | HelloSend contact id |
phone |
string | Phone on the contact record |
firstName |
string | |
lastName |
string | |
email |
string | |
integId |
string | Integration id |
rightServiceId |
string | Messaging service id |
remoteContactId |
string | CRM-side contact id |
Webhooks (REST Hooks)
HelloSend delivers webhook payloads to HTTPS URLs registered via these endpoints. Use these routes to subscribe and unsubscribe triggers.
List Hooks
GET /api/v3/hooks
Response 200 — hooks[] item
| Field | Type | Description |
|---|---|---|
id |
string | Hook subscription id |
event |
string | Event key |
targetUrl |
string | Subscriber HTTPS URL |
status |
string | e.g. active |
apiKeyId |
string | Key used to create the subscription |
createdTime |
string | ISO 8601 timestamp |
Subscribe
POST /api/v3/hooks
Request body
| Field | Required | Description |
|---|---|---|
event |
Yes | Supported event name (case-insensitive) |
targetUrl |
Yes | Absolute https:// URL to receive POST payloads |
Response 201 — Same shape as a single hook object above.
Multiple subscriptions for the same event are supported (each subscriber receives independent deliveries).
Example
{
"event": "inbound_message",
"targetUrl": "https://hooks.zapier.com/hooks/catch/123456/abcdef/"
}
Unsubscribe
DELETE /api/v3/hooks/{hookId}
Response 200
{
"deleted": true,
"hookId": "hook_uuid_here"
}
Errors: 404 hook_not_found when the id does not belong to the authenticated user.
Webhook Samples
Read-only endpoints that return recent webhook-shaped payloads for integration testing. The JSON structure matches live webhook POST bodies.
Inbound SMS Samples
GET /api/v3/zapier-hook-samples/inbound-message-sms
Outbound Delivery Status Samples
GET /api/v3/zapier-hook-samples/outbound-message-sms-status
Query parameters (both endpoints)
| Param | Required | Default | Max |
|---|---|---|---|
limit |
No | 25 | 50 |
Response 200
{
"events": [ /* webhook envelope objects, newest first */ ]
}
Returns { "events": [] } when the account has no recent matching history.
Webhook Events
When a subscribed event occurs, HelloSend POSTs a JSON envelope to the registered targetUrl with Content-Type: application/json.
Supported Events
| Event Key | When It Fires |
|---|---|
inbound_message |
HelloSend receives an inbound SMS on the account |
outbound_message_status |
An outbound SMS reaches a terminal delivery status (delivered or failed) |
Envelope Shape
| Field | Type | Description |
|---|---|---|
id |
string | Unique event id (use for deduplication) |
event |
string | Event key |
occurredAt |
string | ISO 8601 UTC timestamp |
data |
object | Event-specific payload |
inbound_message — data object
| Field | Type | Description |
|---|---|---|
messageId |
string | Provider message id |
fromNumber |
string | Sender (external party) |
toNumber |
string | Your HelloSend number |
body |
string | Message text |
receivedAt |
integer | Unix ms when received |
contact |
object | Matched contact, if any (see below) |
Example payload
{
"id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event": "inbound_message",
"occurredAt": "2026-04-20T12:34:56.789Z",
"data": {
"messageId": "msg_inbound_123",
"fromNumber": "+14155551234",
"toNumber": "+14155559876",
"body": "Hey — reply when you can.",
"receivedAt": 1745148896789,
"contact": {
"phone": "+14155551234",
"firstName": "Ada",
"lastName": "Lovelace",
"email": "ada@example.com",
"remoteContactId": "crm_contact_456"
}
}
}
outbound_message_status — data object
| Field | Type | Description |
|---|---|---|
messageId |
string | Outbound message id |
fromNumber |
string | Your HelloSend number |
toNumber |
string | Recipient |
deliveryStatus |
string | delivered or failed |
loggedAt |
integer | Unix ms inbox log time |
errorCode |
string | Provider error code (when failed) |
errorDetail |
string | Human-readable failure detail (when failed) |
contact |
object | Matched contact, if any |
Example payload
{
"id": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_delivered",
"event": "outbound_message_status",
"occurredAt": "2026-04-20T12:35:00.000Z",
"data": {
"messageId": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"fromNumber": "+14155559876",
"toNumber": "+14155551234",
"deliveryStatus": "delivered",
"loggedAt": 1745152500000,
"errorCode": "",
"errorDetail": "",
"contact": {
"phone": "+14155551234",
"firstName": "Ada",
"lastName": "Lovelace",
"email": "ada@example.com",
"remoteContactId": "crm_contact_456"
}
}
}
Contact Object
Included in both event payloads when a matching contact is found.
| Field | Type | Description |
|---|---|---|
phone |
string | Contact phone |
firstName |
string | |
lastName |
string | |
email |
string | |
remoteContactId |
string | CRM record id |
Zapier Integration Mapping
| Zapier Step | API Calls |
|---|---|
| Connect Account (auth test) | GET /api/v3/auth/verify |
| Trigger: New Inbound SMS | Subscribe inbound_message; poll GET /api/v3/zapier-hook-samples/inbound-message-sms |
| Trigger: Outbound SMS Delivery Status | Subscribe outbound_message_status; poll GET /api/v3/zapier-hook-samples/outbound-message-sms-status |
| Action: Send Message | POST /api/v3/messages (+ Idempotency-Key); dropdowns via GET /api/v3/numbers, GET /api/v3/templates, GET /api/v3/templates/{id} |
| Search: Find Contact by Phone | GET /api/v3/contacts?phone= |
Getting an API Key
API keys are created in the HelloSend web app — not via the API.
- Sign in at https://www.hellosend.com
- Go to Settings → API Keys
- Click Generate key, name it, and copy the secret immediately (shown once)
- Paste the key into your integration
Revoke unused keys from the same Settings page.
Endpoint Index
| Method | Path | Description |
|---|---|---|
| GET | /api/v3/auth/verify |
Verify API key; get connection metadata |
| GET | /api/v3/numbers |
List sendable phone numbers |
| GET | /api/v3/templates |
List message templates |
| GET | /api/v3/templates/{id} |
Get template with placeholders |
| POST | /api/v3/messages |
Send outbound message |
| GET | /api/v3/contacts?phone= |
Find contacts by phone number |
| GET | /api/v3/hooks |
List webhook subscriptions |
| POST | /api/v3/hooks |
Subscribe to a webhook event |
| DELETE | /api/v3/hooks/{hookId} |
Unsubscribe from a webhook |
| GET | /api/v3/zapier-hook-samples/inbound-message-sms |
Recent inbound SMS webhook samples |
| GET | /api/v3/zapier-hook-samples/outbound-message-sms-status |
Recent outbound status webhook samples |