Skip to content

What Are Webhooks?

A webhook lets Werk24 push extraction results to your server once a drawing has been processed. Instead of waiting for messages over a persistent connection, you register a URL and handle the incoming HTTP requests in your own application. This pattern is especially useful when your backend is written in another language—such as Java, JavaScript/Node.js, C#, Go, PHP, Ruby, or Rust—because all you need to implement is a standard HTTPS endpoint.

Register a Callback

Use the read_drawing_with_callback method of Werk24Client to send a drawing and register your callback URL:

from werk24 import Werk24Client, AskMetaData

async def submit(drawing_bytes: bytes):
    async with Werk24Client() as client:
        request_id = await client.read_drawing_with_callback(
            drawing_bytes,
            [AskMetaData()],
            "https://example.com/webhook",
            callback_headers={"X-Token": "my-shared-secret"},
        )
    print(f"Registered request: {request_id}")

Node.js equivalent

import fetch from 'node-fetch';
import FormData from 'form-data';
import fs from 'fs';

const LICENSE_TOKEN = 'YOUR_WERK24_TOKEN';
const SECRET = 'my-shared-secret';

async function submit() {
  const form = new FormData();
  form.append('drawing', fs.createReadStream('drawing.pdf'));
  form.append('asks', JSON.stringify([{ ask_version: 'v2', ask_type: 'META_DATA' }]));
  form.append('callback_url', JSON.stringify('https://example.com/webhook'));
  form.append('callback_headers', JSON.stringify({ 'X-Token': SECRET }));
  form.append('max_pages', JSON.stringify(5));

  const res = await fetch('https://api.w24.co/techread/read-with-callback', {
    method: 'POST',
    headers: { ...form.getHeaders(), Authorization: `Token ${LICENSE_TOKEN}` },
    body: form,
  });

  if (!res.ok) {
    // A drawing too large for the request body (about 4.6 MB, see
    // "Size limit" below) is refused here, with no request_id.
    throw new Error(`Submission failed: HTTP ${res.status}`);
  }

  const { request_id } = await res.json();
  console.log('Registered request:', request_id);
}

submit().catch(console.error);

Parameters

  • callback_url – URL that will receive POST requests containing TechreadMessage objects, several per read (see below).
  • callback_headers – Optional headers included with the request. Headers must start with X- or be explicitly whitelisted (e.g., Authorization).
  • public_key – Optional PEM encoded public key used to encrypt result files, for accounts with end-to-end encryption.

The method returns a request_id which you can use to correlate the asynchronous callbacks with your system.

Size limit: about 4.6 MB

A callback request carries the drawing inside its own body, and that body is limited to 6 MiB after base64 encoding: about 4.6 MB, shared with the other form fields. That is less than the 10 MiB that read_drawing allows, because read_drawing uploads the drawing separately. From versions newer than 2.7.0, the Python client refuses an oversized request before sending it, with CallbackDrawingTooLargeException or CallbackFieldsTooLargeException (both subclasses of RequestTooLargeException). Without the client, check the size yourself. For larger drawings, use read_drawing. See Drawing File Size Limit.

What arrives at your callback URL

One read arrives as several POSTs, not one: PROGRESS / STARTED first, then one or more ASK messages for every ask you sent (up to 8 in flight at once, in any order), and PROGRESS / COMPLETED last. The read is done when COMPLETED arrives. Collect the ASK messages under their request_id until then.

Delivery is at least once. Answer each POST with a 2xx within 10 seconds. A connection failure, a timeout or a 408, 425, 429, 500, 502, 503 or 504 is retried, up to 3 attempts in about 20 seconds. Every attempt carries the header X-Werk24-Delivery-Attempt (1, then 2, 3), so a redelivery can be recognised; it has an identical body, so deduplicate on the body. Any other non-2xx answer, or a message whose attempts all failed, ends the read, and the read stays charged.

The HTTP API page shows every message with its headers and body, and how long a payload_url stays valid.

Works with Any Stack

Because callbacks are just HTTPS requests, you can receive them in virtually any programming language.

Handle the Callback in Node.js

Here is a minimal Express server that logs the incoming message and verifies a custom header:

const express = require('express');
const app = express();

app.use(express.json());

const SECRET = 'my-shared-secret';

app.post('/webhook', (req, res) => {
  if (req.get('X-Token') !== SECRET) {
    return res.status(401).send('Unauthorized');
  }

  const { request_id, message_subtype, payload_dict } = req.body;
  console.log('Received:', request_id, message_subtype, payload_dict);
  res.sendStatus(200);
});

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

Expose this endpoint over HTTPS and set the callback URL to https://your-host/webhook when submitting the drawing.

Supported languages include:

  • Java
  • JavaScript / Node.js
  • TypeScript
  • C#
  • Go
  • PHP
  • Ruby
  • Rust
  • C++

When to Use Webhooks

Webhooks are ideal for integrations where long‑lived connections are impractical or when you do not want to run a Python client. Because Werk24 sends results via ordinary HTTP POST requests, any language or framework that can handle web requests can consume them—for example Java, JavaScript/Node.js, TypeScript, C#, Go, PHP, Ruby, or Rust. Register the callback and process the results whenever Werk24 calls your endpoint.