API engineering / Field notes 01

Scanner API integration: design the workflow before the endpoint

A practical architecture for scan jobs, evidence, retries, and policy decisions—without turning uncertainty into a green check.

Scanner API typography card: Scan. Understand. Decide.

Connecting a scanner looks simple until the first timeout, duplicate submission, or incomplete result reaches production. A client sends an object; an engine returns something that looks like a verdict. Between those two events, your application still has to answer who owns the object, which version was inspected, what the engine actually checked, and whether the result permits the next action.

A dependable scanner API integration makes those questions explicit. The design below is a proposed architecture, not a description of a hosted endpoint on ScannerAPI.com. Use it to structure a service you operate or to evaluate the contract of a provider you are considering. Start with the decision your application must make, then work backward to the evidence needed to support it.

Separate three kinds of success

Transport success, processing success, and a policy decision are different events. A request can arrive successfully while its scan is still queued. A scan can finish successfully and identify something your policy blocks. Conversely, an engine can fail without reaching any conclusion about the content. Naming all three stages prevents the familiar mistake of interpreting a successful HTTP response as permission to release an object.

The HTTP specification’s definition of 202 Accepted describes acceptance for processing, not completed processing. Build an asynchronous integration around a retrievable job resource rather than assuming the original connection will deliver a later verdict. The exact endpoint names are your design choice; the important requirement is that a client can discover the state without creating another scan every time it asks.

Give every job an immutable subject

Record what the scanner is inspecting before you record what it found. For a file, keep a content digest and a storage version. For a repository, use a commit identifier and the selected scope. For a prompt evaluation, identify the conversation or evaluation fixture without unnecessarily copying sensitive text into every log. A human-readable filename is useful context, but it should not be the only binding between a result and an object.

Consider an attachment replaced while a scan is running. The filename might be unchanged even though the bytes differ. A release step that checks only the name could approve a version the engine never saw. Design the release operation to compare the inspected identity with the identity being promoted. When they differ, request a new inspection or hold the object rather than borrowing a result from an earlier version.

Design the result before the request

A useful result envelope can contain a job identifier, subject reference, requested checks, processing status, individual engine outcomes, findings, coverage notes, and policy evaluation. Keep the original evidence reference alongside the normalized fields. Normalization makes the interface easier to operate; retaining provenance prevents the normalization layer from erasing information a specialist needs later.

Avoid inventing a universal risk score merely because a dashboard needs a number. A metadata field, an authorization finding, and an antivirus detection do not necessarily belong on one numeric scale. A better summary can say which required checks completed, which produced findings, and which require review. Let the underlying evidence explain why. The scanner API overview provides a compact checklist for this separation.

Treat coverage as first-class data

Suppose an archive contains several readable files and one encrypted member. “Scan completed” may describe the outer job, but it does not establish complete inspection of every member. Include the skipped component and the reason in the result. A policy can then reject encrypted archives, route them to review, or allow them in a deliberately limited context. What it should not do is lose that distinction during serialization.

Make retries safe and bounded

Assume a client may lose the response after the server has accepted the work. Define how the client can retry without accidentally creating an unbounded number of jobs. One option is a client-generated idempotency key scoped to an owner and request. Store the association long enough to cover the retry window, and specify what happens when the same key is presented with a different subject or different checks.

Polling also needs a budget. Use a documented interval or bounded backoff, stop after a defined deadline, and expose the unresolved state to operations. A permanently running spinner is not a recovery policy. When a deadline expires, retain a record that the decision is pending or incomplete. Do not silently convert the condition into approval just to keep an upload interface moving.

Authenticate events, not just requests

Some integrations offer completion callbacks. Treat those messages as input from another system, not as inherently trusted facts. Verify the provider’s supported authentication mechanism, constrain which events are accepted, and bind the message to a known job. Consider a follow-up result fetch when the provider’s contract supports it, so the application reads the authoritative state through its usual authenticated channel.

Design for duplicate or out-of-order events. A repeated notification should not release the same object twice, overwrite a newer decision, or send multiple contradictory messages to a user. Keep state transitions explicit and reject transitions that do not make sense. For example, a stale “running” message should not replace a later completed state for the same analysis attempt.

Keep policy outside engine adapters

An engine adapter should translate the provider’s response faithfully. The release policy should decide what that response means for your application. Keeping them separate makes it easier to update a scanner without burying product decisions inside parsing code. It also lets the same evidence support different actions in different contexts, such as an internal review queue versus a public download workflow.

Write policy rules in terms a reviewer can explain. “All required checks completed and no blocking finding remains” is easier to audit than an unexplained score threshold. Record the policy version and the evidence used. An override should identify an authorized person, a reason, and an expiration or scope where appropriate. The override is a separate decision; it should not rewrite the original scanner result.

Test failures as carefully as detections

Create a small integration fixture set before launch. Include an accepted request, a duplicate request, an unsupported format, an engine timeout, a malformed provider response, a late completion event, and a subject that changes before release. You do not need live malicious material to test the state machine. Controlled fixtures can establish whether the application responds correctly to each documented outcome.

Then walk through the operational handoff. Can support find the job from the user’s report? Can an operator identify the unavailable engine without reading private file contents? Can the application explain why the object remains held? Record queue age, failure categories, and required-check completion as separate signals. These observations help distinguish an overloaded service from a detection event that genuinely needs security review.

Choose a small first integration

Start with one input type, one clear decision, and a limited set of required checks. A file inspection pipeline is a useful example because staging, inspection, and release can be made explicit. Do not begin by promising that one envelope makes every kind of scanner interchangeable. Expand the schema only when a new use case reveals a meaningful difference that reviewers need to understand.

A well-designed scanner API is a reliable conversation between an application, an inspection engine, and a policy owner. It says what was submitted, what happened, what remains unknown, and why the next action is allowed. That clarity is more valuable than a fast-looking response that compresses uncertainty into a green checkmark.

Continue the thread
Back to The Scan Log