Horizon

Test Autodesk Platform Services webhooks locally

Receive Autodesk Platform Services webhook events on localhost with a Horizon tunnel, and verify the x-adsk-signature header in Next.js.

Receive Autodesk Platform Services (APS) webhook events on your laptop while you build, with a public HTTPS URL APS can reach.

Horizon has no Autodesk integration. The APS Webhooks service sends events to a public URL, and Horizon provides that URL. APS has no dashboard page for webhooks. You register them through the Webhooks API.

Before you begin

  • Node.js 18 or later
  • A Horizon account and the CLI (see Getting started)
  • A reserved subdomain for -s. Reserve one on the Subdomains page.
  • An APS app, and an access token with the data:read and data:write scopes
  • A Next.js app that uses the App Router and runs on port 3000

Start your app

APS signs payloads only when you register a secret token. It then computes an HMAC-SHA1 hex digest of the payload with that token and sends sha1hash=<hex digest> in the x-adsk-signature header. Without a token, APS sends no signature.

Create a route handler. Read the raw body with request.text(), because the signature covers the exact bytes.

app/api/webhooks/autodesk/route.ts
import { createHmac, timingSafeEqual } from "node:crypto";

export async function POST(request: Request) {
  const secret = process.env.APS_WEBHOOK_SECRET;
  if (!secret) {
    return new Response("Missing APS_WEBHOOK_SECRET", { status: 500 });
  }

  const body = await request.text();
  const received = request.headers.get("x-adsk-signature") ?? "";
  const expected = `sha1hash=${createHmac("sha1", secret).update(body).digest("hex")}`;

  const receivedBuffer = Buffer.from(received);
  const expectedBuffer = Buffer.from(expected);
  const isValid =
    receivedBuffer.length === expectedBuffer.length &&
    timingSafeEqual(receivedBuffer, expectedBuffer);

  if (!isValid) {
    return new Response("Invalid signature", { status: 401 });
  }

  console.log("Received APS webhook:", body);

  return new Response("ok", { status: 200 });
}

APS expects a 2xx response within 6 seconds. Keep the handler fast.

Pick a random alphanumeric secret between 32 and 64 characters, and store it:

.env.local
APS_WEBHOOK_SECRET=replace-with-a-32-to-64-character-alphanumeric-string

Start the app:

npm run dev

Start a tunnel

In a second terminal, open a tunnel to port 3000 on a subdomain you reserved, with -s:

hrzn tunnel http://localhost:3000 -s my-aps-app
Output
HORIZON: Tunnel connected
  URL          https://my-aps-app.hrzn.run (reserved)
  Forwarding   http://localhost:3000
  Request log  https://hrzn.run/dashboard/tunnels/my-aps-app

Your public URL is https://my-aps-app.hrzn.run. Without -s the subdomain is random and changes on every run, so a registered hook would point at a dead URL after a restart. Reserved subdomains are a paid feature, see Pricing.

Register the secret token

Send the secret to the Webhooks API with POST /webhooks/v1/tokens. Replace <ACCESS_TOKEN> with your access token and the token value with your secret.

curl -X POST "https://developer.api.autodesk.com/webhooks/v1/tokens" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{ "token": "replace-with-a-32-to-64-character-alphanumeric-string" }'

From now on, callbacks carry the x-adsk-signature header.

Create the hook

Create a hook for a Data Management event. This example listens for dm.version.added in one folder. Set callbackUrl to your tunnel URL plus the route path, and scope.folder to the URN of the folder to watch.

curl -X POST "https://developer.api.autodesk.com/webhooks/v1/systems/data/events/dm.version.added/hooks" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "callbackUrl": "https://my-aps-app.hrzn.run/api/webhooks/autodesk",
    "scope": { "folder": "<FOLDER_URN>" }
  }'

A successful call returns 201 Created with the hook's details.

Trigger an event

APS has no resend tool. Do what the event watches for. For dm.version.added, upload a new file or a new version of a file to the folder. APS then calls your URL.

Check it works

After you add a version to the folder, the Horizon terminal prints one line:

Output
  POST    200  /api/webhooks/autodesk

Your app terminal prints the payload as Received APS webhook: {...}.

If the line shows [401], see Troubleshooting.

Troubleshooting

The signature doesn't match

The Horizon line shows [401]. Check these in order:

  • APS_WEBHOOK_SECRET is exactly the token you sent to POST /webhooks/v1/tokens. Restart npm run dev after you edit .env.local.
  • The handler hashes the raw body from request.text(). Parsing the JSON first and hashing the object gives a different value.
  • You compare against sha1hash= followed by the hex digest, not the digest alone.

There is no x-adsk-signature header

You didn't register a secret token, or the token call failed. Send the token to POST /webhooks/v1/tokens and check for a success response.

The hook stops delivering

APS retries a failed callback four times, and the last retry is at least 48 hours after the first attempt. If the last retry fails, APS disables the hook. A failure is a non-2xx status, or no response within 6 seconds. Fix the handler, then create or reactivate the hook.

The URL changed after a restart

You started the tunnel without -s, so Horizon gave you a new random subdomain. The hook still calls the old URL. Restart with -s my-aps-app, and create the hook again if the URL differs.

Next steps

On this page