Skip to content

What are Hooks?

The Werk24 API is asynchronous: lightweight results (like Page Thumbnails) are ready very quickly (≈ 300–500 ms), while heavy analyses (e.g., AskInsights) may take longer (up to \~50 s for complex drawings).

To keep your integration simple, Werk24 provides Hooks. A Hook ties a specific Ask (the what you want) to a function (the what to do when it’s ready). When the requested data becomes available, your function is called with the result—no polling loops needed on your side.


Key Concepts

  • Ask A declarative request for a specific artifact (e.g., W24AskPageThumbnail, W24AskVariantMeasures). You may register many Asks per file.

  • Hook A (ask, function) pair. The function runs once per matching result. Hooks are evaluated independently—fast results arrive first.

  • Asynchrony Results are produced and delivered as they’re ready. There’s no guaranteed order between different Asks.

  • Idempotency Your functions should tolerate duplicates (e.g., on client restarts). Write hooks so re-execution doesn’t break anything.


Typical Latencies (Guidance)

  • Page Thumbnail: 0.3–0.5 s
  • Metadata: \~4–10 s
  • Features: \~6–15 s (complexity-dependent)
  • Insights: \~6–25 s (complexity-dependent)

These are not SLAs, just planning numbers to design UX and timeouts.


Delivery & Ordering Guarantees

  • Each Hook fires as soon as its Ask is ready.
  • Hooks for different Asks are independent and may fire in any order.
  • If a failure occurs for a given Ask, you’ll receive an error (see below).
  • Avoid shared mutable state between hook functions unless you protect it.

Example: Using Hooks in Python

Suppose you want to (i) save a page thumbnail and (ii) print extracted measures.

from werk24 import Hook, W24AskPageThumbnail, W24AskVariantMeasures

def save_file(result):
    # result may contain bytes and metadata; adapt to your SDK object shape
    content = getattr(result, "content", None) or getattr(result, "bytes", None)
    name = getattr(result, "filename", "page_thumbnail.png")
    if content:
        with open(name, "wb") as f:
            f.write(content)
        print(f"Saved: {name}")

def print_measures(result):
    # Prefer robust printing; large payloads -> summarize
    measures = getattr(result, "measures", None) or result
    print(f"Received {len(measures) if hasattr(measures, '__len__') else 'measures'}")

hooks = [
    Hook(ask=W24AskPageThumbnail(), function=save_file),
    Hook(ask=W24AskVariantMeasures(), function=print_measures),
]

This setup triggers save_file as soon as the thumbnail is available, and print_measures when measures arrive.


End-to-End Sketch (Upload + Hooks + Run)

from Werk24 import W24TechreadClient, W24Ask, Hook, W24AskPageThumbnail, W24AskVariantMeasures

client = W24TechreadClient(api_key="YOUR_KEY")

def save_thumbnail(res):
    # idempotent write
    path = "page-0-thumb.png"
    data = getattr(res, "content", None)
    if data and not (os.path.exists(path) and len(open(path, "rb").read()) == len(data)):
        with open(path, "wb") as f:
            f.write(data)
    print(f"Thumbnail saved to {path}")

def log_measures(res):
    # Avoid dumping huge JSON to stdout in production
    count = getattr(res, "count", None) or len(getattr(res, "measures", []) or [])
    print(f"Measures ready (count ~ {count})")

hooks = [
    Hook(ask=W24AskPageThumbnail(), function=save_thumbnail),
    Hook(ask=W24AskVariantMeasures(), function=log_measures),
]

with open("drawing.pdf", "rb") as f:
    # The client typically accepts file-like objects and a list of asks/hooks
    client.process(f, hooks=hooks)

Tip: Keep hook functions fast. If you do I/O or heavy post-processing, hand off to a worker queue so you don’t block other callbacks.


Designing Good Hooks

  • Idempotent Safe to call more than once (e.g., check if file exists before writing; use upserts).

  • Stateless or isolated state Keep per-file state keyed by a stable ID to avoid cross-talk.

  • Non-blocking Offload long work. Hooks should act like interrupt handlers: quick, reliable, minimal.

  • Defensive Validate payload shape; tolerate missing optional fields.


Backpressure & Concurrency

  • If you register many Hooks across many files, your own callback work can become the bottleneck.
  • Use a thread pool or async queue to keep callbacks snappy:

  • CPU-boundconcurrent.futures.ThreadPoolExecutor or ProcessPoolExecutor

  • I/O-boundasyncio.create_task + bounded asyncio.Semaphore

FAQs

Do Hooks guarantee order? No. Each Hook fires when its Ask is ready. Don’t rely on ordering.

Can I run multiple Hooks for the same Ask? Yes—register multiple Hooks targeting the same Ask if you need to fan out actions (e.g., save + notify).

How do I limit callback fan-out? Wrap heavy logic in a queue/worker with bounded concurrency; keep the Hook handler tiny.

What if I only want the first page thumbnail? Scope your Ask accordingly (e.g., page index parameters if available). Otherwise, discard in the callback.

Can I correlate results back to my upload? Yes. Use stable identifiers (upload ID, filename, or a client-side UUID) passed along in your closure or captured in your hook function.


Best-Practice Checklist

  • [ ] Separate Asks (what you need) from Hooks (what to do).
  • [ ] Keep Hook functions idempotent and fast.
  • [ ] Expect out-of-order arrivals; don’t share mutable state.
  • [ ] Handle errors explicitly; log and retry where it’s safe.
  • [ ] Use queues/executors for heavy post-processing.
  • [ ] Provide great UX: show the thumbnail first, stream progress, reveal heavy results when ready.