All API endpoints require two forms of authentication:
1. Bearer Token
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
2. Request Signing
Include both of these headers on every request:
X-Signature: <HMAC-SHA256 hex-encoded signature>
X-Timestamp: <Unix timestamp in milliseconds>
The signature payload differs by HTTP method:
- POST/PUT/PATCH/DELETE requests: Sign
{timestamp}.{body}
- GET requests: Sign
{timestamp}.{uri} (full path + query string)
For multipart/form-data requests, sign the exact raw request body bytes with a timestamp prefix: {timestamp}.<raw-bytes>.
Requests with timestamps older than 5 minutes are rejected.
How to calculate
- Get current Unix timestamp in milliseconds.
- Build payload:
- POST/PUT/PATCH/DELETE:
{timestamp}.{raw-body}
- GET:
{timestamp}.{uri} (for example /v2/members?limit=10&offset=0)
- Calculate HMAC-SHA256 of the payload using your API secret.
- Hex-encode the digest (64 lowercase chars).
- Send both
X-Signature and X-Timestamp.
Example: POST request (Python)
For POST/PUT/PATCH/DELETE requests, sign {timestamp}.{body}:
import json
import hmac
import hashlib
import time
import requests
def create_topic(name: str, members: list[str], description: str = None, external_id: str = None) -> dict:
"""Create a topic in Zenzap with HMAC signature."""
# Build request body
body = {
"name": name,
"members": members,
}
if description:
body["description"] = description
if external_id:
body["externalId"] = external_id
# Serialize body (no spaces for consistent signing)
body_json = json.dumps(body, separators=(",", ":"))
# Timestamp for replay protection
timestamp = int(time.time() * 1000)
# Create HMAC-SHA256 signature from "{timestamp}.{body}"
signature_payload = f"{timestamp}.{body_json}"
signature = hmac.new(
API_SECRET.encode(),
signature_payload.encode(),
hashlib.sha256
).hexdigest()
# Make request
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"X-Signature": signature,
"X-Timestamp": str(timestamp),
}
response = requests.post(f"{BASE_URL}/v2/topics", headers=headers, data=body_json)
response.raise_for_status()
return response.json()
Example: GET request (Python)
For GET requests, sign {timestamp}.{uri} (including query string):
import hmac
import hashlib
import time
import requests
def get_topic(topic_id: str) -> dict:
"""Get a specific topic from Zenzap with HMAC signature."""
# For GET requests, sign the URI path (including query string)
uri_path = f"/v2/topics/{topic_id}"
# Timestamp for replay protection
timestamp = int(time.time() * 1000)
# Create HMAC-SHA256 signature from "{timestamp}.{uri}"
signature_payload = f"{timestamp}.{uri_path}"
signature = hmac.new(
API_SECRET.encode(),
signature_payload.encode(),
hashlib.sha256
).hexdigest()
# Make request
headers = {
"Authorization": f"Bearer {API_KEY}",
"X-Signature": signature,
"X-Timestamp": str(timestamp),
}
response = requests.get(f"{BASE_URL}/v2/topics/{topic_id}", headers=headers)
response.raise_for_status()
return response.json()
Our API documentation tools cannot automatically generate HMAC signatures. You will need to calculate this manually or use a tool like Postman with pre-request scripts.