Agent skill

Workato Webhooks Skill

Receive and authenticate requests sent by Workato recipes. Use when building an endpoint that a Workato HTTP connector action calls, securing it with the HTTP connection's Basic auth username and password, debugging 401s from Workato, or routing recipe payloads by an event field such as order.created or record.updated.

Install this skill

npx skills add hookdeck/webhook-skills --skill workato-webhooks


Workato is an automation platform with no native outbound webhooks. A recipe pushes data to your app with the HTTP connector's Send request via HTTP action, using an HTTP connection set to Basic authentication. Workato does not sign these requests, so your endpoint authenticates them with the Basic auth username and password and routes them by an event field you put in the body.

When to Use This Skill

  • How do I receive webhooks from a Workato recipe?
  • How do I secure an endpoint that Workato's HTTP connector calls?
  • How do I verify the Authorization: Basic header Workato sends?
  • Does Workato sign webhook requests? (No: static Basic auth credential)
  • Why does my Workato HTTP action get a 401 from my webhook endpoint?

Verification (core)

Parse Authorization: Basic base64(username:password) (scheme is case-insensitive), split on the first colon, and compare the username and the password each in constant time. Evaluate both before deciding:

const crypto = require('crypto');

function safeEqual(a, b) {
  const ba = Buffer.from(String(a), 'utf8');
  const bb = Buffer.from(String(b), 'utf8');
  if (ba.length !== bb.length) return false;       // timingSafeEqual needs equal lengths
  return crypto.timingSafeEqual(ba, bb);
}

function verifyWorkatoRequest(headers, user, password) {
  if (!user || !password) return false;            // never accept when unconfigured
  const m = (headers['authorization'] || '').match(/^Basic\s+([A-Za-z0-9+/]+={0,2})\s*$/i);
  if (!m) return false;
  const decoded = Buffer.from(m[1].trim(), 'base64').toString('utf8');
  const sep = decoded.indexOf(':');                // password may contain ':'
  if (sep === -1) return false;
  const userOk = safeEqual(decoded.slice(0, sep), user);
  const passwordOk = safeEqual(decoded.slice(sep + 1), password);
  return userOk && passwordOk;                     // no short-circuit above
}

Return 401 when this fails, 400 for a non-empty body that isn't a JSON object, and 200 once handled. Authenticate before parsing the body, and read it raw (express.raw({ type: '*/*' })) so any content type works. Python and Next.js equivalents are in references/verification.md.

A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS only, and rotate the password if it leaks.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

HTTP Methods and Empty Bodies

The method is whatever the recipe's HTTP action is set to, so the examples accept POST, PUT, PATCH, GET and DELETE on the route and treat an empty body (typical for GET) as {}.

Event Types

Workato defines no webhook event types; the payload is whatever the recipe sends. Send {"event": "...", "data": {...}} and dispatch on event. The examples use these names, which are a convention, not Workato values:

EventSent When
order.createdA recipe sees a new order
record.updatedA recipe syncs a changed record
recipe.failedAn error-handling step reports a failure

Environment Variables

WORKATO_WEBHOOK_USERNAME=workato                              # "Basic auth user"
WORKATO_WEBHOOK_PASSWORD=replace-with-a-long-random-password  # "Basic auth password"

Local Development

Workato can't reach localhost. Tunnel with the Hookdeck CLI (no account required):

npx hookdeck-cli listen 3000 workato --path /webhooks/workato

Use the printed URL as the HTTP connection's Base URL.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 5, 2026

View on GitHub →