Skip to main content
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

  1. Get current Unix timestamp in milliseconds.
  2. Build payload:
    • POST/PUT/PATCH/DELETE: {timestamp}.{raw-body}
    • GET: {timestamp}.{uri} (for example /v2/members?limit=10&offset=0)
  3. Calculate HMAC-SHA256 of the payload using your API secret.
  4. Hex-encode the digest (64 lowercase chars).
  5. 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.