Back to Blog
Engineering

Lisa Chen8 min
Del

When your agent runs a task, Coasty sends events via Server-Sent Events. Trusting these payloads requires proof of origin. HMAC signatures let you verify that each event really came from Coasty. You avoid accepting tampered data or replayed payloads from an attacker.

How webhook verification works

Coasty signs each webhook event header with HMAC. The header name is Coasty-Signature. It contains a timestamp and a hex signature. The signature is computed over the request body using a secret key. The secret key is the key you generated in your Coasty dashboard. You must store it securely and never expose it in client-side code. When you receive a webhook, extract the body, timestamp, and signature. Compute the expected HMAC and compare it against the received value. Reject the event if the timestamps are too far in the past or the signatures mismatch.

python
import os
import hmac
import hashlib
import json
from datetime import datetime, timedelta

API_KEY = os.getenv("COASTY_API_KEY")  # your webhook secret key
ALLOWED_TOLERANCE_SECONDS = 300  # 5 minutes

def verify_webhook(request_body: bytes, header_signature: str) -> bool:
    # Parse timestamp and signature from header
    # Format: t=<unix_time>,v1=<hex_signature>
    try:
        ts_str, sig_str = header_signature.split(",", 1)
        timestamp = int(ts_str.split("=")[1])
        received_sig = sig_str.split("=")[1]
    except (IndexError, ValueError) as e:
        print(f"Invalid signature header format: {e}")
        return False

    # Check clock skew
    now = int(datetime.utcnow().timestamp())
    if abs(now - timestamp) > ALLOWED_TOLERANCE_SECONDS:
        print(f"Timestamp too old or future: {timestamp}")
        return False

    # Compute expected HMAC
    secret = API_KEY.encode("utf-8")
    digest = hmac.new(secret, request_body, hashlib.sha256).hexdigest()

    # Compare signatures in constant time
    if not hmac.compare_digest(received_sig, digest):
        print("HMAC signature mismatch")
        return False

    return True

# Example: Flask route
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhooks/coasty", methods=["POST"])
def cozy_webhook():
    body = request.get_data()
    signature = request.headers.get("Coasty-Signature")

    if not signature:
        return jsonify({"error": "Missing Coasty-Signature"}), 401

    if not verify_webhook(body, signature):
        return jsonify({"error": "Invalid signature"}), 401

    # Parse event payload
    event = json.loads(body.decode("utf-8"))
    event_type = event.get("type")
    run_id = event.get("run_id")

    # Handle the event (e.g., update your database, notify your UI)
    print(f"Event {event_type} for run {run_id}")

    return jsonify({"status": "received"})

if __name__ == "__main__":
    app.run(port=5000)

Key webhook fields and states

  • The Coasty-Signature header contains t=<unix_timestamp>,v1=<hex_signature>.
  • Timestamp is the current Unix seconds value at the time Coasty sent the event.
  • Run states include queued, running, awaiting_human, succeeded, failed, cancelled, and timed_out.
  • Events stream from GET /v1/runs/{id}/events with Last-Event-ID for reconnection.
  • Each webhook payload includes run_id, type (e.g., run_started, run_finished), and optional agent actions or error details.
  • The secret key used for signing is the same key you configure in your Coasty developer dashboard.

Always verify the Coasty-Signature header and timestamp before processing any webhook payload.

Where this beats brittle automation

Traditional automation relies on brittle selectors or fixed API endpoints. Selectors break when UI changes. API-only tools lack visibility into the actual desktop. With a computer use agent, Coasty drives real applications and browsers. Webhooks give you real-time visibility into run progress and outcomes. HMAC signatures ensure these events are genuine. You can safely integrate run state into your own systems without trusting random payloads. This is especially important for long-running workflows and human-in-the-loop approvals where rejecting a tampered event can prevent costly errors.

You now know how to verify Coasty webhook payloads with HMAC signatures. Build a secure receiver that trusts Coasty’s events, preventing replay and tampering. Use this to power dashboards, alerting, and state synchronization for your computer use agents. Ready to start? Get your key at https://coasty.ai/developers.

© 2026 Coasty

Backed byYCombinator