Waggish TravelsDevelopersInteractive API referenceopenapi v2openapi v1

Request signing

When your API client has "Require HMAC-signed write requests" enabled, every POST, PUT, PATCH and DELETE must carry a signature. GET requests never need one. Signing protects against tampering and replay: a captured request cannot be re-sent later.

You receive a signing secret (whsig_…) together with each API key. It is shown once.

Headers

Header Value
X-Timestamp Current Unix time in seconds
X-Signature Lowercase hex HMAC-SHA256 of the string below, keyed with your signing secret

String to sign

<timestamp>\n<HTTP METHOD>\n<request path and query>\n<raw request body>

Node.js

import crypto from "node:crypto";

async function signedPost(path, payload, apiKey, signingSecret) {
  const body = JSON.stringify(payload);
  const ts = Math.floor(Date.now() / 1000);
  const signature = crypto
    .createHmac("sha256", signingSecret)
    .update(`${ts}\nPOST\n${path}\n${body}`)
    .digest("hex");

  return fetch(`https://searchurtrip.com${path}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${apiKey}`,
      "X-Timestamp": String(ts),
      "X-Signature": signature,
      "Idempotency-Key": crypto.randomUUID(),
    },
    body,
  });
}

Python

import hashlib, hmac, json, time, uuid, requests

def signed_post(path, payload, api_key, signing_secret):
    body = json.dumps(payload, separators=(",", ":"))
    ts = str(int(time.time()))
    to_sign = f"{ts}\nPOST\n{path}\n{body}"
    sig = hmac.new(signing_secret.encode(), to_sign.encode(), hashlib.sha256).hexdigest()
    return requests.post(
        "https://searchurtrip.com" + path,
        data=body,
        headers={
            "Content-Type": "application/json",
            "Authorization": f"Bearer {api_key}",
            "X-Timestamp": ts,
            "X-Signature": sig,
            "Idempotency-Key": str(uuid.uuid4()),
        },
    )

Failure modes

Cause Response
Missing X-Timestamp / X-Signature 401 unauthorized — "Signed requests require…"
Timestamp outside the 5-minute window 401 unauthorized
Signature does not match 401 unauthorized — "Invalid request signature"

The most common mistake is signing a differently formatted body than the one sent (whitespace, key order). Serialise once and reuse the same string.