Engineering

Verifying Computer Use API Webhooks with HMAC Signatures

Daniel Kim||8 min
+W

When you submit a computer use task run via POST /v1/runs, the Coasty computer use agent executes actions on a real desktop, browser, or terminal. You receive a webhook_url to be notified of changes in state, including queued, running, awaiting_human, succeeded, failed, cancelled, and timed_out. Because webhooks can be spoofed or tampered with, every payload must be verified. Coasty signs webhook POST requests with an HMAC SHA256 signature and the header Coasty-Signature: t=unix,v1=hex. This guide shows how to validate those signatures in Node.js so you can safely consume task run events.

How it works

Coasty signs each webhook POST request that includes the header Coasty-Signature: t=unix,v1=hex. The header contains two parts: t is the request timestamp in seconds, and v1 is the hex-encoded HMAC-SHA256 of the full request body using your API key as the secret. Your server must verify that the timestamp is recent (to prevent replay attacks) and that the HMAC matches the body. If both checks pass, you can trust the event. This pattern ensures that only an entity holding your secret can produce valid signatures.

javascript
// Install dependencies: npm install crypto dotenv express
require('dotenv').config();
const crypto = require('crypto');
const express = require('express');
const app = express();

// Body parser to read incoming JSON payloads
app.use(express.json());

// Replace this with your secret key from https://coasty.ai/developers/keys
const COASTY_API_KEY = process.env.COASTY_API_KEY || 'your-key-here';

// Expected time window in seconds for timestamp validation
const MAX_TOLERANCE_SECONDS = 300;

app.post('/webhook/coasty', (req, res) => {
  const signatureHeader = req.headers['coasty-signature'];
  const body = JSON.stringify(req.body);

  if (!signatureHeader) {
    return res.status(401).json({ error: 'Missing signature' });
  }

  // Parse Coasty-Signature header: t=unix,v1=hex
  const [tPart, v1Part] = signatureHeader.split(',');
  const timestamp = parseInt(tPart.split('=')[1], 10);
  const hmac = v1Part.split('=')[1];

  // Verify timestamp freshness
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - timestamp) > MAX_TOLERANCE_SECONDS) {
    return res.status(401).json({ error: 'Timestamp too old' });
  }

  // Compute HMAC using your secret key
  const hmacBuffer = crypto
    .createHmac('sha256', COASTY_API_KEY)
    .update(body)
    .digest();
  const computedHmac = hmacBuffer.toString('hex');

  // Compare in constant time to prevent timing attacks
  if (crypto.timingSafeEqual(Buffer.from(hmac, 'hex'), Buffer.from(computedHmac, 'hex'))) {
    // Signature is valid; you can trust the payload
    console.log('Valid webhook received:', req.body);
    res.status(200).json({ received: true });
  } else {
    res.status(401).json({ error: 'Invalid signature' });
  }
});

app.listen(3000, () => console.log('Webhook receiver running on port 3000'));

// Expected environment variable:
// export COASTY_API_KEY=your-actual-api-key-from-coasty-ai

Webhook payload structure

  • Coasty sends a POST request to the webhook_url you provided when creating a task run via POST /v1/runs.
  • The body is a JSON object representing the run state. It includes fields like id, status, error (if present), and metadata about the machine and agent.
  • The header Coasty-Signature: t=unix,v1=hex is used for HMAC verification. t is the request timestamp in seconds, v1 is the hex-encoded HMAC-SHA256 of the full request body.
  • Each webhook event may be delivered multiple times for a single run. Your receiver should be idempotent, processing events with the same run id only once per state transition.

The signature header is always Coasty-Signature: t=unix,v1=hex, where the HMAC is computed over the full JSON request body using your API key as the secret.

Where this beats brittle automation

Most browser automation tools rely on brittle selectors like CSS classes, IDs, or XPath expressions. When a site updates its UI, those selectors break and your tests flake. The Coasty computer use agent sees the screen exactly like a human, reads text, recognizes buttons, and navigates the UI naturally. Webhooks give you a reliable, tamper-evident stream of run events so you can react to success or failure without polling. This approach is resilient to UI changes, works across browsers and OS, and lets you integrate directly into CI, ticketing, or monitoring systems.

Next steps

  • Use the webhook receiver above as the foundation for your Coasty integration.
  • Subscribe to the /v1/runs/{id}/events Server-Sent Events stream as an alternative or complement to webhooks for real-time state updates.
  • Read the full API reference at https://coasty.ai/docs and generate your API key at https://coasty.ai/developers/keys.
  • Start building production workflows that drive agents on real machines and trust every event because it is HMAC-signed.

Webhook verification is essential for secure, production-grade computer use integrations. With HMAC signatures, you can trust every event from the Coasty agent and build reliable automation that reacts to success or failure without flaking. Get your API key at https://coasty.ai/developers and start verifying webhooks today.

Want to see this in action?

View Case Studies
Try Coasty Free