API Authentication¶
All requests to the Abelo REST API must be authenticated. Abelo supports two authentication mechanisms:
- Bearer Token Authentication — the simplest option and recommended for most server-to-server integrations.
- HMAC-SHA256 Request Signing — provides additional request integrity and protection against unauthorized request modification or replay when stronger authentication controls are required.
1. Generating API Keys¶
API keys are created and managed in the Abelo CMS.
- Log in to the Abelo dashboard and navigate to Settings > Developer > API Keys. This requires the Owner or Admin role.
- Click Create API Key.
- Enter a descriptive label for the key, such as
Production BackendorStaging Integration. - Select the authentication method (Bearer or HMAC) and assign the required API scopes.
- Create the key and securely store the generated Key ID, Secret and Webhook Signing Secret. You can use the latter in case you need to override target URL of a given webhook for messaging events. Check more in Triggering API Campaig
Store Secrets Securely
API secrets are displayed only once when the key is created. Abelo stores secrets securely using Argon2id hashing and does not allow them to be retrieved later through the CMS or API.
If a secret is lost or compromised, revoke or rotate the affected API key and generate a new one.
Never Expose API Secrets
API credentials are intended for server-side use only. Never include API secrets in client-side JavaScript, mobile applications, browser code, source repositories, or other publicly accessible locations.
2. Authentication Methods¶
Method 1: Bearer Token Authentication (Recommended)¶
Bearer Token Authentication is the simplest authentication method and is recommended for most server-to-server integrations.
Include the API credentials in the Authorization header using the following format:
Example Request¶
Method 2: HMAC-SHA256 Request Signing¶
For enhanced security, HMAC-SHA256 Request Signing verifies message integrity and prevents replay attacks by signing the timestamp, HTTP method, request path, and request body with your hmac_secret.
Required Headers¶
| Header | Description | Example |
|---|---|---|
X-Abelo-Key-Id |
The public Key ID | ak_01J8K92M4 |
X-Abelo-Timestamp |
Unix timestamp in seconds (UTC) of request. Used for defense against intercepted replayed requests. | 1724164200 |
X-Abelo-Signature |
Hex-encoded HMAC-SHA256 signature | sha256=a1b2c3d4... |
Signature Canonical String¶
The signature is computed over the following payload:
For requests without a body (e.g.
GETorDELETE), use an empty string for{RAW_REQUEST_BODY}:
{timestamp}.GET./v1/profiles.
Python Signing Implementation¶
import hashlib
import hmac
import time
import httpx
from typing import Any, Dict, Optional
import json
KEY_ID = "ak_01J8K92M4"
HMAC_SECRET = "hs_9f8a7b6c5d4e3f2a1b0c"
API_URL = "https://api.abelo.ai"
def generate_auth_headers(method: str, path: str, body_bytes: bytes) -> dict[str, str]:
timestamp = str(int(time.time()))
body_hash = hashlib.sha256(body_bytes).hexdigest()
sig_string = f"{timestamp}\n{method.upper()}\n{path}\n{body_hash}"
signature = hmac.new(
key=HMAC_SECRET.encode("utf-8"),
msg=sig_string.encode("utf-8"),
digestmod=hashlib.sha256
).hexdigest()
return {
"X-Abelo-Key-Id": KEY_ID,
"X-Abelo-Timestamp": timestamp,
"X-Abelo-Signature": signature,
"Content-Type": "application/json"
}
def dispatch(method: str, path: str, payload: Optional[Dict[str, Any]] = None) -> httpx.Response:
url = f"{API_URL}{path}"
# Prepare the exact bytes that will be sent over the wire.
# Using separators=(',', ':') removes whitespace, making the JSON compact
# and preventing hash mismatches caused by different JSON formatters.
if payload is not None and method.upper() not in ["GET", "HEAD"]:
body_bytes = json.dumps(payload, separators=(',', ':')).encode("utf-8")
else:
body_bytes = b""
headers = generate_auth_headers(method, path, body_bytes)
with httpx.Client() as _client:
request = _client.build_request(
method=method,
url=url,
content=body_bytes if body_bytes else None,
headers=headers
)
return _client.send(request)
print("Dispatching GET...")
get_response = dispatch(
method="GET",
path="/v1/healthcheck"
)
print(f"GET Response Status: {get_response.status_code}")
print(f"GET Response Body: {get_response.json()}")
print("Dispatching POST...")
post_response = dispatch(
method="POST",
path="/v1/messages/send",
payload={
"campaign_id":"cmp_01J8K92M4",
"recipient": {
"type":"phone_number",
"value":"+306912345678"
}
}
)
print(f"POST Response Status: {post_response.status_code}")
print(f"POST Response Body: {post_response.json()}\n")
print(post_response.status_code, post_response.json())
Replay Attack Window
The timestamp header must be within 60 seconds of server time. Requests received outside this validity window return 401 Unauthorized (Request expired).
3. API Scopes & Access Control¶
Each API key is restricted to the specific scopes assigned to it:
| Scope | Description | Granted Endpoints |
|---|---|---|
messages:send |
Trigger API campaigns | POST /v1/messages/send |
profiles:read |
Query and retrieve profiles | GET /v1/profilesGET /v1/profiles/{public_id} |
profiles:write |
Create, update, and delete profiles | POST /v1/profilesPATCH /v1/profiles/{public_id}DELETE /v1/profiles/{public_id} |
webhooks:manage |
Manage webhooks | GET /v1/webhooksPOST /v1/webhooksPATCH /v1/webhooks/{id}DELETE /v1/webhooks/{id}POST /v1/webhooks/{id}/rotate-secretPOST /v1/webhooks/test |