Skip to content

Webhook Signatures

PromptJang signs every outbound webhook delivery with HMAC-SHA256. This lets your target endpoint verify that the payload came through PromptJang and wasn't tampered with.

How It Works

When PromptJang delivers a webhook to your target endpoint, we add these headers:

HeaderDescription
X-PromptJang-SignatureHMAC-SHA256 hex digest of timestamp.raw_body
X-PromptJang-TimestampUnix timestamp (seconds) when the delivery was made
X-PromptJang-Event-IDUnique event ID — use for idempotency
X-PromptJang-Event-TypeEvent type (if set)
X-Correlation-IDYour correlation ID (if provided when sending)

Signing Algorithm

timestamp = current_unix_seconds()
signature = HMAC-SHA256(endpoint_secret, "${timestamp}.${raw_body}")

The endpoint secret is generated when you create an endpoint and returned in the response.

Verifying on Your Target Endpoint

python
import hmac, hashlib, time

def verify_webhook(request):
    signature = request.headers['X-PromptJang-Signature']
    timestamp = request.headers['X-PromptJang-Timestamp']
    body = request.raw_body

    # 1. Check timestamp freshness (reject stale deliveries)
    if abs(time.time() - int(timestamp)) > 300:
        return 401, "Stale timestamp"

    # 2. Reconstruct the signed content
    signed_content = f"{timestamp}.".encode() + body

    # 3. Compute expected signature
    expected = hmac.new(
        ENDPOINT_SECRET.encode(),
        signed_content,
        hashlib.sha256
    ).hexdigest()

    # 4. Compare (constant-time)
    if not hmac.compare_digest(expected, signature):
        return 401, "Invalid signature"

    # 5. Process the event
    event_id = request.headers['X-PromptJang-Event-ID']
    if already_processed(event_id):
        return 200  # Already handled — idempotent

    process_event(event_id, body)
    return 200

Idempotency

Every delivery includes X-PromptJang-Event-ID. Your target endpoint should use this to avoid processing the same event twice:

  • Retries: If we retry and your endpoint already processed the event, return 200
  • Network blips: If your server processed it but the connection dropped before we got the response
  • Replay: Manual replays get a new event ID, so they're treated as new events

Optional: Inbound Signature Verification

By default, sending events to PromptJang only requires an API key. For security-sensitive integrations, PromptJang can be configured to require HMAC signatures on inbound events too (the same algorithm). Contact support to enable this.

When enabled, you must include X-PromptJang-Signature and X-PromptJang-Timestamp headers when sending events to POST /e/:endpoint_id, signing with the same endpoint secret.

Released under the MIT License.