Documentation
API contract (OpenAPI)
This page is the human-friendly view: Swagger UI to browse endpoints, plus the YAML source for reading or copying.
The URL /docs/openapi.yaml is the machine-readable file the explorer loads automatically. It is meant for tools (Postman, Insomnia, OpenAPI Generator, CI) and normally downloads as a file— not as a full-screen “black page” in the browser.
Interactive reference
Swagger UI loads the contract from the same endpoint as the download link above.
Loading interactive API reference…
Source YAML
For diffs, codegen, or pasting into an editor. Expand to read inline with site styling.
Show full YAML
openapi: 3.1.0
info:
title: ADA PDF Gatekeeper API
version: "1.0.0"
description: >
REST API for in-memory PDF accessibility analysis and remediation. Designed for zero-storage,
privacy-first integrations (CMS plugins, workers, backends).
Recommended single-step flow: POST /v1/process returns analysis plus remediation in one response,
with an optional temporary download URL for an accessibility-enhanced PDF.
This process improves accessibility and helps meet ADA/WCAG guidelines, but does not constitute
a formal compliance certification. Outputs are not PDF/UA guarantees and OCR-rebuilt documents
may differ visually from the source.
Error responses use `{ "error": true, "message": "..." }` unless noted.
When `REQUIRE_API_KEY_FOR_BATCH_AND_REMEDIATE=false` on the server, `POST /v1/batch`,
`POST /v1/remediate`, and `GET /v1/batch/{batch_id}` may be called without an API key
(development only).
servers:
- url: https://api.example.com
description: Production
- url: http://127.0.0.1:3002
description: Local development
components:
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
schemas:
ProcessingStatus:
type: string
enum: [processing, completed, failed]
ComplianceStatus:
type: string
nullable: true
enum: [compliant, partially_compliant, not_compliant]
ScanIssue:
type: object
properties:
id:
type: string
severity:
type: string
enum: [critical, warning, info]
title:
type: string
description:
type: string
recommendation:
type: string
required: [id, severity, title, description, recommendation]
ScanIssuesBySeverity:
type: object
properties:
critical:
type: array
items:
$ref: "#/components/schemas/ScanIssue"
warning:
type: array
items:
$ref: "#/components/schemas/ScanIssue"
info:
type: array
items:
$ref: "#/components/schemas/ScanIssue"
required: [critical, warning, info]
ScanSource:
type: object
properties:
type:
type: string
enum: [upload, url]
label:
type: string
required: [type, label]
ScanResult:
type: object
properties:
id:
type: string
source:
$ref: "#/components/schemas/ScanSource"
processing_status:
$ref: "#/components/schemas/ProcessingStatus"
compliance_status:
$ref: "#/components/schemas/ComplianceStatus"
score:
type: integer
format: int32
nullable: true
summary:
type: string
nullable: true
issues:
allOf:
- $ref: "#/components/schemas/ScanIssuesBySeverity"
nullable: true
createdAt:
type: string
format: date-time
required:
[id, source, processing_status, compliance_status, score, summary, issues, createdAt]
ScanListItem:
type: object
properties:
id:
type: string
source:
$ref: "#/components/schemas/ScanSource"
processing_status:
$ref: "#/components/schemas/ProcessingStatus"
compliance_status:
$ref: "#/components/schemas/ComplianceStatus"
score:
type: integer
format: int32
nullable: true
createdAt:
type: string
format: date-time
required: [id, source, processing_status, compliance_status, score, createdAt]
ErrorResponse:
type: object
properties:
error:
type: boolean
example: true
message:
type: string
required: [error, message]
HealthOkResponse:
type: object
properties:
status:
type: string
enum: [ok]
required: [status]
HtmlPdfLink:
type: object
properties:
url:
type: string
format: uri
label:
type: string
required: [url, label]
UrlAnalysisResult:
type: object
properties:
type:
type: string
enum: [url_analysis]
classification:
type: string
enum: [pdf, single_page, site_scan, protected]
pdf_links:
type: array
items:
$ref: "#/components/schemas/HtmlPdfLink"
pdf_count:
type: integer
format: int32
requires_auth:
type: boolean
domain:
type: string
required:
[type, classification, pdf_links, pdf_count, requires_auth, domain]
RemediationStatus:
type: string
description: Outcome of remediation (generation of an accessibility-enhanced document) for this request or batch item.
enum: [completed, failed, unavailable]
BatchMode:
type: string
enum: [audit, remediate]
BatchStatus:
type: string
enum: [processing, completed, partial, failed]
BatchItemStatus:
type: string
enum: [processing, completed, failed]
BatchItem:
type: object
properties:
url:
type: string
scan_id:
type: string
nullable: true
status:
$ref: "#/components/schemas/BatchItemStatus"
score:
type: integer
format: int32
nullable: true
compliance_status:
$ref: "#/components/schemas/ComplianceStatus"
remediation_status:
type: string
nullable: true
description: >
In remediate mode, typically completed, failed, or unavailable; null in audit mode.
remediation_summary:
type: string
nullable: true
description: Human-readable remediation outcome when applicable.
download_url:
type: string
nullable: true
description: >
Temporary link to download the accessibility-enhanced PDF produced by remediation, when available.
required:
[
url,
scan_id,
status,
score,
compliance_status,
remediation_status,
remediation_summary,
download_url,
]
RemediateResponse:
type: object
properties:
remediation_status:
$ref: "#/components/schemas/RemediationStatus"
remediation_summary:
type: string
description: Human-readable summary of remediation (accessibility-enhanced document generation) or why it did not complete.
download_url:
type: string
format: uri
nullable: true
description: >
Temporary link to download the accessibility-enhanced PDF generated by the system when remediation succeeds.
Set API_PUBLIC_URL so clients receive a browser-reachable origin.
required: [remediation_status, remediation_summary, download_url]
ProcessUnifiedStatus:
type: string
enum: [completed, partial, failed]
description: >
completed: analysis succeeded and remediation produced a download_url for an accessibility-enhanced PDF.
partial: analysis succeeded but remediation did not yield a downloadable document (or the remediation request failed).
failed: analysis did not complete (remediation is skipped).
ProcessAnalysisBlock:
type: object
nullable: true
properties:
score:
type: integer
format: int32
compliance_status:
$ref: "#/components/schemas/ComplianceStatus"
issues:
allOf:
- $ref: "#/components/schemas/ScanIssuesBySeverity"
nullable: true
summary:
type: string
required: [score, compliance_status, issues, summary]
ProcessRemediationBlock:
type: object
properties:
status:
type: string
description: Processor remediation_status, failed if the API could not call the processor, or skipped when analysis failed.
summary:
type: string
download_url:
type: string
format: uri
nullable: true
description: >
Temporary link to download the accessibility-enhanced PDF generated by the system when present.
required: [status, summary, download_url]
ProcessResponse:
type: object
properties:
status:
$ref: "#/components/schemas/ProcessUnifiedStatus"
analysis:
allOf:
- $ref: "#/components/schemas/ProcessAnalysisBlock"
nullable: true
remediation:
$ref: "#/components/schemas/ProcessRemediationBlock"
required: [status, analysis, remediation]
Batch:
type: object
properties:
batch_id:
type: string
mode:
$ref: "#/components/schemas/BatchMode"
status:
$ref: "#/components/schemas/BatchStatus"
total:
type: integer
format: int32
processed:
type: integer
format: int32
createdAt:
type: string
format: date-time
items:
type: array
items:
$ref: "#/components/schemas/BatchItem"
required: [batch_id, mode, status, total, processed, createdAt, items]
BatchCreateResponse:
type: object
properties:
batch_id:
type: string
status:
$ref: "#/components/schemas/BatchStatus"
required: [batch_id, status]
paths:
/health:
get:
summary: Liveness check
description: No API key required. Intended for load balancers and orchestrators.
security: []
responses:
"200":
description: Service is alive
content:
application/json:
schema:
$ref: "#/components/schemas/HealthOkResponse"
/v1/status:
get:
summary: Service identification
description: No API key required.
security: []
responses:
"200":
description: Service is healthy
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
service:
type: string
required: [ok, service]
/v1/scans/upload:
post:
summary: Scan a PDF uploaded as a file
security:
- apiKey: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
required: [file]
responses:
"200":
description: Scan completed successfully
content:
application/json:
schema:
$ref: "#/components/schemas/ScanResult"
"400":
description: Invalid request (missing file, not a PDF, etc.)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Invalid or missing API key
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"413":
description: PDF exceeds max size (default 25 MB; `MAX_PDF_BYTES`)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limited
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"504":
description: Processor analyze request timed out (`PROCESSOR_ANALYZE_TIMEOUT_MS`)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"502":
description: Processor error or scan could not be completed
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/scans/url:
post:
summary: Scan a PDF available at a public URL
security:
- apiKey: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
required: [url]
responses:
"200":
description: Direct PDF scan result or HTML page analysis for batch/UI flows
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ScanResult"
- $ref: "#/components/schemas/UrlAnalysisResult"
"400":
description: Invalid URL or the URL did not return a PDF
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Invalid or missing API key
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"413":
description: Downloaded PDF exceeds max size (`MAX_PDF_BYTES`)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limited
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"502":
description: Scan did not complete (e.g. processor error)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/scans/async:
post:
summary: Start an asynchronous scan from a file upload or URL
description: >
Starts a scan and returns immediately. Poll GET /v1/scans/{scanId} to
retrieve the final result.
security:
- apiKey: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
responses:
"202":
description: Scan accepted for processing
content:
application/json:
schema:
type: object
properties:
scan_id:
type: string
processing_status:
$ref: "#/components/schemas/ProcessingStatus"
required: [scan_id, processing_status]
"400":
description: Invalid request (missing file or URL, invalid URL, not a PDF)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Invalid or missing API key
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"413":
description: PDF exceeds max size (`MAX_PDF_BYTES`)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limited
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/process:
post:
summary: Single-step analysis + remediation (recommended)
description: >
Runs the same analysis as POST /v1/scans/upload, then remediation as POST /v1/remediate on the same PDF.
Returns one JSON object with the analysis report and remediation outcome, plus an optional temporary
download URL for an accessibility-enhanced PDF (not a compliance certificate).
Does not create a scan record in GET /v1/scans. Requires a valid API key (same as scan routes).
security:
- apiKey: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: PDF file (use this or url field, not both).
url:
type: string
format: uri
description: Direct PDF URL (multipart alternative to file).
options:
type: string
description: Optional JSON string, e.g. {"forceOcr":true} to force the OCR rebuild path on the processor.
application/json:
schema:
type: object
properties:
pdfBase64:
type: string
description: Base64-encoded PDF (use this or url, not both).
url:
type: string
format: uri
description: Direct PDF URL.
options:
type: object
properties:
forceOcr:
type: boolean
description: When true, processor uses the OCR rebuild path even for text-classified PDFs (requires Tesseract + Poppler).
responses:
"200":
description: Unified result (completed, partial, or failed envelope)
content:
application/json:
schema:
$ref: "#/components/schemas/ProcessResponse"
"400":
description: Invalid request (missing input, not a PDF, URL not a direct PDF, etc.)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Invalid or missing API key
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"413":
description: PDF exceeds max size
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ErrorResponse"
- $ref: "#/components/schemas/ProcessResponse"
"429":
description: Rate limited
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"502":
description: Analyze or remediate processor error (body may be ProcessResponse or ErrorResponse)
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ErrorResponse"
- $ref: "#/components/schemas/ProcessResponse"
"504":
description: Processor timeout
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ErrorResponse"
- $ref: "#/components/schemas/ProcessResponse"
/v1/remediate:
post:
summary: Remediation — accessibility-enhanced PDF (OCR for image-heavy docs; metadata pass for text-based)
description: >
Generates an accessibility-enhanced PDF when possible: text-based inputs are processed for better metadata;
image-heavy inputs use OCR where dependencies are installed. Does not certify ADA/WCAG compliance or full PDF/UA structure.
security:
- apiKey: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
pdfBase64:
type: string
url:
type: string
format: uri
responses:
"200":
description: Remediation result (analysis is not included; use POST /v1/process for combined analysis + remediation).
content:
application/json:
schema:
$ref: "#/components/schemas/RemediateResponse"
"400":
description: Invalid request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Invalid or missing API key (unless server disables key for this route)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"413":
description: PDF exceeds max size (`MAX_PDF_BYTES`)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limited
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"504":
description: Processor remediate timed out (`PROCESSOR_REMEDIATE_TIMEOUT_MS`)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"502":
description: Processor unavailable
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/remediate/download/{token}:
get:
summary: Download a temporarily stored accessibility-enhanced PDF (token + TTL; no API key for browser compatibility)
parameters:
- name: token
in: path
required: true
schema:
type: string
responses:
"200":
description: Accessibility-enhanced PDF binary (processed for better accessibility; not a compliance certificate).
content:
application/pdf:
schema:
type: string
format: binary
"404":
description: Expired or unknown token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/batch:
post:
summary: Start batch analysis or analysis+remediation for up to 20 direct PDF URLs
description: >
`audit` returns analysis per URL; `remediate` adds remediation when possible.
Download URLs point to accessibility-enhanced PDFs, not certified compliance documents.
security:
- apiKey: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
mode:
$ref: "#/components/schemas/BatchMode"
urls:
type: array
maxItems: 20
items:
type: string
required: [mode, urls]
responses:
"200":
description: Batch accepted for processing
content:
application/json:
schema:
$ref: "#/components/schemas/BatchCreateResponse"
"400":
description: Invalid request (validation, max URL limit, etc.)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Invalid or missing API key (unless server disables key for this route)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limited
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/batch/{batch_id}:
get:
summary: Get the full state of a batch job (analysis and optional remediation per item)
security:
- apiKey: []
parameters:
- name: batch_id
in: path
required: true
schema:
type: string
responses:
"200":
description: Batch found
content:
application/json:
schema:
$ref: "#/components/schemas/Batch"
"401":
description: Invalid or missing API key (unless server disables key for this route)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: Batch not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/scans/{scanId}:
get:
summary: Get a single scan by id
security:
- apiKey: []
parameters:
- name: scanId
in: path
required: true
schema:
type: string
responses:
"200":
description: Scan found
content:
application/json:
schema:
$ref: "#/components/schemas/ScanResult"
"401":
description: Invalid or missing API key
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: Scan not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/v1/scans:
get:
summary: List recent scans for the current API key
security:
- apiKey: []
responses:
"200":
description: List of scans
content:
application/json:
schema:
type: object
properties:
scans:
type: array
items:
$ref: "#/components/schemas/ScanListItem"
"401":
description: Invalid or missing API key
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limited
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"