Engineering

Verify Computer Use API Webhooks with HMAC Signatures

Lisa Chen||6 min
F12

When you configure a webhook_url on a task run, Coasty sends a signed Server-Sent Event stream when the agent reaches a new state. If you do not verify the signature, a malicious actor could replay events or inject fake status updates to your backend. The Coasty API includes HMAC signing for webhooks so you can validate each event and only act on data you trust.

How webhook signing works

All webhooks sent to your endpoint include the header Coasty-Signature with the format t=UNIX_TIMESTAMP,v1=HEX_STRING. Coasty signs the raw event body using HMAC-SHA256 with your API key as the secret. Your server recomputes the signature using the same secret and compares it against the received value. If they match, the event is authentic. If not, reject the payload and log the request_id from the error object for debugging.

python
import hmac
import hashlib
import os
import base64
import json
from datetime import datetime

COASTY_API_KEY = os.getenv("COASTY_API_KEY")
WEBHOOK_SECRET = COASTY_API_KEY  # Coasty uses the key as the HMAC secret

def verify_webhook_signature(payload_bytes, received_header):
    # Expected header format: t=UNIX_TIMESTAMP,v1=HEX_STRING
    try:
        timestamp_str, signature_str = received_header.split(",")
        received_timestamp = int(timestamp_str.split("=")[1])
        received_signature = signature_str.split("=")[1]
    except Exception as e:
        raise ValueError("Invalid Coasty-Signature header format")

    # Compute HMAC-SHA256 of the raw payload
    computed_signature = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload_bytes,
        hashlib.sha256
    ).hexdigest()

    # Compare signatures in constant time to prevent timing attacks
    if not hmac.compare_digest(computed_signature, received_signature):
        raise ValueError("Signature mismatch")

    # Optional: check timestamp freshness (e.g., allow at most 10 minutes skew)
    current_unix = int(datetime.utcnow().timestamp())
    if abs(current_unix - received_timestamp) > 600:
        raise ValueError("Timestamp too old or in the future")

    return True

# Example: Flask webhook endpoint
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhook/coasty", methods=["POST"])
def handle_webhook():
    raw_body = request.get_data()
    signature_header = request.headers.get("Coasty-Signature")

    try:
        verify_webhook_signature(raw_body, signature_header)
        payload = json.loads(raw_body)
        event_type = payload.get("event_type")
        data = payload.get("data")
        request_id = payload.get("request_id")

        # Process authenticated event
        if event_type == "task_run_updated":
            status = data.get("status")
            print(f"Task run {request_id} updated to {status}")
            # Your integration logic goes here

        return jsonify({"status": "received"})
    except Exception as e:
        print(f"Webhook verification failed for request_id {request_id}: {e}")
        return jsonify({"error": str(e)}), 401

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

Key webhook fields and headers

  • Header Coasty-Signature: t=UNIX_TIMESTAMP,v1=HEX_STRING for HMAC-SHA256 verification.
  • Body: Server-Sent Events stream containing event_type, data, and request_id.
  • Timestamp: UNIX seconds since epoch to detect replay attacks.
  • Auth: Use X-API-Key or Authorization: Bearer for all other API calls.

Always compute the signature from the raw request body and compare with Coasty-Signature in constant time.

Where computer use beats brittle automation

Browser automation with selectors often breaks when UI changes or when elements are nested inside iframes. The Coasty computer use agent drives real desktops and browsers by seeing the screen and acting like a human. Webhooks let you react in real time to agent state changes without polling. You can build workflows that pause for human approval, cancel on errors, or notify your team, all while avoiding fragile selectors.

Next steps

Set up a webhook_url when you POST to /v1/runs. Keep your API key secure and use it only for HMAC signing. Explore workflows and task runs to orchestrate multi-step automation. Get your key and start building secure integrations at https://coasty.ai/developers.

Want to see this in action?

View Case Studies
Try Coasty Free