{"openapi":"3.1.0","info":{"title":"Pixel Public API","version":"1.0.0","description":"Customer-facing API for file assessment and result retrieval. Every result surface returns the same JobResult shape, and every endpoint emits the same error taxonomy."},"servers":[{"url":"https://api.pixelsystems.com","description":"Production"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"API key JWT token obtained via organization provisioning"}},"schemas":{"ErrorCode":{"type":"string","enum":["invalid_request","unsupported_format","unauthorized","forbidden","not_found","file_too_large","unprocessable","rate_limited","internal_error","service_unavailable"],"description":"Stable machine-readable error code. The same vocabulary is used by every endpoint and by jobs in `error` or `rejected` status."},"ApiError":{"type":"object","properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable explanation of the error."},"details":{"type":"object","additionalProperties":{},"description":"Optional structured context for the error (e.g. the offending field)."},"retryAfter":{"type":"integer","exclusiveMinimum":0,"description":"Seconds to wait before retrying. Set for `rate_limited` and `service_unavailable`."}},"required":["code","message"],"description":"Standard error response returned by every public endpoint (Decision 5C)."},"JobStatus":{"type":"string","enum":["processing","completed","error","rejected"],"description":"Lifecycle status of a job. `verdict` is present only when `completed`. `rejected` is reserved for future use and is not currently produced."},"ConfidenceLevel":{"type":"string","enum":["high","medium","low"],"description":"Qualitative confidence in an assessment."},"VerdictDisplay":{"type":"object","properties":{"label":{"type":"string","minLength":1,"description":"Short badge or table-cell copy, e.g. `Inconclusive`."},"title":{"type":"string","description":"Headline for the main result indicator."},"description":{"type":"string","description":"Supporting sentence shown under the headline."}},"required":["label"],"description":"Rendered copy for `assessment`. Present for every completed run."},"Dimension":{"type":"object","properties":{"key":{"type":"string","description":"Stable identifier of the dimension, e.g. `ai_detection`."},"label":{"type":"string","description":"Human-readable name of the dimension."},"assessment":{"type":"string","description":"Aggregated assessment for this dimension."},"score":{"type":"number","minimum":0,"maximum":1,"description":"Aggregated numeric score in [0, 1]."},"confidence":{"$ref":"#/components/schemas/ConfidenceLevel"}},"required":["key","label","score"],"description":"An axis of analysis carrying its own aggregated result."},"Verdict":{"type":"object","properties":{"assessment":{"type":"string","description":"Headline, customer-actionable assessment (e.g. `ai_generated`)."},"confidence":{"$ref":"#/components/schemas/ConfidenceLevel"},"display":{"$ref":"#/components/schemas/VerdictDisplay"},"dimensions":{"type":"array","items":{"$ref":"#/components/schemas/Dimension"}}},"required":["assessment","confidence","dimensions"],"description":"Overall verdict for a completed job, with per-dimension breakdown. Present only when `status` is `completed`."},"JobResultError":{"type":"object","properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string"}},"required":["code","message"],"description":"Why a job ended in `error` or `rejected` status; present only for those statuses. Uses the same code vocabulary as endpoint errors."},"JobResult":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid","description":"Single job identity, shared by every path that references this job."},"status":{"$ref":"#/components/schemas/JobStatus"},"verdict":{"$ref":"#/components/schemas/Verdict"},"error":{"$ref":"#/components/schemas/JobResultError"},"metadata":{"type":"object","additionalProperties":{},"description":"Caller-supplied metadata, echoed back verbatim."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created (ISO 8601)."},"completedAt":{"type":"string","format":"date-time","description":"When processing finished (ISO 8601)."}},"required":["jobId","status","createdAt"],"description":"The v1 job result. The same object is returned by the synchronous response, the poll, and every future result surface."},"AssessRequest":{"type":"object","properties":{"file":{"type":"string","description":"The file to assess.","format":"binary"},"metadata":{"type":"string","description":"Optional JSON-encoded object, echoed back verbatim as `metadata` on the result."}},"required":["file"],"description":"Multipart submission. Parts may appear in any order (Decision 5D)."},"PaginationMeta":{"type":"object","properties":{"page":{"type":"integer","minimum":1},"pageSize":{"type":"integer","minimum":1},"total":{"type":"integer","minimum":0},"totalPages":{"type":"integer","minimum":0},"hasNextPage":{"type":"boolean"}},"required":["page","pageSize","total","totalPages","hasNextPage"]},"GetJobsListResponse":{"type":"object","properties":{"jobs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"applicationId":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}},"createdAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","applicationId","metadata","createdAt","updatedAt"]}},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["jobs","pagination"]}},"parameters":{}},"paths":{"/v1/assess":{"post":{"operationId":"assess","summary":"Assess a file","tags":["public"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"boolean","default":true,"description":"Block until the job completes. `true` (the default) waits up to 30 s before falling back to the polling response; `false` returns immediately. Changes response timing and HTTP status only — never the result shape."},"required":false,"name":"wait","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Optional idempotency key. Retrying a submission with the same key returns the original job instead of creating a duplicate."},"required":false,"name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AssessRequest"}}}},"responses":{"200":{"description":"Assessment completed within the wait window","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResult"}}}},"202":{"description":"Job accepted and still processing (`wait=false` or wait timeout); poll `GET /v1/jobs/{jobId}`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResult"}}}},"400":{"description":"`invalid_request` or `unsupported_format`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"`file_too_large` — exceeds the size limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"`unprocessable` — valid file but cannot be analysed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"`rate_limited` — too many requests; `retryAfter` is set","headers":{"RateLimit-Limit":{"schema":{"type":"string","description":"Request ceiling for the current window"},"required":true},"RateLimit-Remaining":{"schema":{"type":"string","description":"Requests remaining in the current window"},"required":true},"Retry-After":{"schema":{"type":"string","description":"Seconds to wait before retrying"},"required":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"`service_unavailable` — temporary overload; retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/jobs":{"get":{"operationId":"listJobs","summary":"List jobs","tags":["public"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"applicationId","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"default":100,"description":"Number of jobs per page. Values above 100 are clamped to 100, not rejected."},"required":false,"name":"pageSize","in":"query"}],"responses":{"200":{"description":"Paginated list of jobs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetJobsListResponse"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"`rate_limited` — too many requests; `retryAfter` is set","headers":{"RateLimit-Limit":{"schema":{"type":"string","description":"Request ceiling for the current window"},"required":true},"RateLimit-Remaining":{"schema":{"type":"string","description":"Requests remaining in the current window"},"required":true},"Retry-After":{"schema":{"type":"string","description":"Seconds to wait before retrying"},"required":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/jobs/{jobId}":{"get":{"operationId":"getJob","summary":"Get a job's result","tags":["public"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"jobId","in":"path"},{"schema":{"type":"boolean","default":true,"description":"Block until the job completes. `true` (the default) waits up to 30 s before falling back to the polling response; `false` returns immediately. Changes response timing and HTTP status only — never the result shape."},"required":false,"name":"wait","in":"query"}],"responses":{"200":{"description":"Result for the job (poll or long-poll)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResult"}}}},"400":{"description":"`invalid_request`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"`not_found` — job does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"`rate_limited` — too many requests; `retryAfter` is set","headers":{"RateLimit-Limit":{"schema":{"type":"string","description":"Request ceiling for the current window"},"required":true},"RateLimit-Remaining":{"schema":{"type":"string","description":"Requests remaining in the current window"},"required":true},"Retry-After":{"schema":{"type":"string","description":"Seconds to wait before retrying"},"required":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"webhooks":{}}