Design the workflow.
Keep the evidence.
Integration patterns for systems you build or evaluate. Model the input, preserve each result, and keep permission decisions separate from detection signals.
Normalize the shape.
Not the uncertainty.
The JSON example is a proposed application model, not documentation for a hosted ScannerAPI.com endpoint. The copy control copies only this local example to your clipboard; it does not send a request.
Start with an immutable subject reference and separate processing state, per-check outcomes, and policy. A completed outer job can still have partial inspection coverage. The example therefore requests review even though one required check reports no detection.
Keep provider-specific evidence alongside your normalized fields. An adapter should faithfully describe what happened; the application policy should determine the next action.
Read the architecture walkthrough{
"example": true,
"subject": {
"kind": "file",
"reference": "example-object-v1"
},
"processing": {
"state": "completed",
"coverage": "partial"
},
"checks": [
{
"kind": "antivirus",
"outcome": "no_detection"
},
{
"kind": "archive_contents",
"outcome": "unsupported"
}
],
"policy": {
"decision": "review",
"reason": "Required coverage is incomplete"
}
}Example data for integration design. No request is sent.
Give each field one job.
| Field | Meaning in this example | Why preserve it |
|---|---|---|
subject.reference | An immutable object or version reference. | Prevents an old result from approving changed content. |
processing.state | The lifecycle of the inspection job. | Keeps queued, running, completed, and failed work distinct. |
processing.coverage | A summary of what was actually inspected. | Makes partial or unsupported inspection visible. |
checks[].outcome | A faithful result from a named check. | Preserves no detection, detection, failure, and unsupported outcomes. |
policy.decision | The application’s release or review action. | Separates evidence from business policy and overrides. |
Make every transition explicit.
Choose names that fit your service. The important part is that clients and operators can distinguish unfinished work from a completed decision.
Identify and queue.
Validate the caller, bind the subject, record requested checks, and return a retrievable job reference. A successful submission is not a release decision.
Track each check.
Keep time limits, unsupported inputs, retries, and engine-specific outcomes visible. Preserve the reason when the work cannot finish.
Apply the policy.
Evaluate the required evidence, record the policy version, and bind the resulting action to the inspected object. Do not rewrite evidence to justify an override.
Plan the unhappy path.
Retries and duplicate events
Define whether a repeated request refers to the same work. Scope an idempotency key to the owner and request, document its lifetime, and reject conflicting reuse. Treat repeated or out-of-order completion events as normal integration cases to test.
Bounded waiting
Choose a polling or event-delivery strategy from the provider’s actual contract. Set a deadline and expose unresolved work to an owner. Do not silently approve an object because the scanner is unavailable.
Private evidence
Log references and outcomes rather than unnecessary raw files, prompts, or credentials. Protect detailed evidence separately and give reviewers only the access needed for the task.
Test the interpretation.
Use harmless fixtures and synthetic results to exercise an accepted job, an unsupported input, an engine error, a delayed completion, a duplicate event, and a subject that changes before release.
For each case, specify the expected processing state, the policy decision, the storage visibility, and the user message. A transport-level success should never be the only acceptance criterion.
Review provider details
Check supported inputs, authentication, data-sharing terms, engine update behavior, quotas, event verification, and result retention directly in the provider’s documentation. These properties belong to the selected service; the example model does not create them.
Open the primary source library