{
  "openapi": "3.1.0",
  "info": {
    "title": "Screenata Auditor Pull API",
    "version": "1.0.0-preview.1",
    "summary": "Read-only API for audit firms to pull evidence, statuses, controls, and policies from the audit cycles they have been granted access to.",
    "description": "The Auditor Pull API (`audit-api/1.0`) lets an invited auditor's own tooling — scripts, workpaper systems, CI jobs — pull engagement data from Screenata instead of clicking through the Auditor Center portal.\n\n**Status: preview.** This contract is published ahead of general availability so audit firms can review it and build against it during the pilot. Endpoints are additive-only within `/v1` once live; anything breaking goes to `/v2`. To join the pilot, contact security@screenata.com.\n\n**Authentication.** Every request presents a personal API token (`sk_audit_…`) as a Bearer token. Tokens are minted by the auditor from the Auditor Center, are bound to that auditor's seat in the customer workspace, and grant nothing beyond what the auditor's login already has: per-cycle access is re-checked on every request, and revoking the auditor's access revokes the token's reach instantly. Tokens are accepted only in the `Authorization` header, never as query parameters.\n\n**Transparency.** Every read is written to the customer's auditor access log — the customer sees exactly which resources each token pulled, and when.\n\n**Verification.** Evidence file responses carry an `X-Content-Sha256` header and listings include `contentSha256`, so pulled artifacts can be verified byte-for-byte. Audit packages carry a `manifestHash` over their contents.\n\n**Read-only.** There are no write endpoints in v1. Comments, evidence requests, and review actions remain in the Auditor Center portal.",
    "contact": {
      "name": "Screenata",
      "url": "https://screenata.com/audit-api",
      "email": "security@screenata.com"
    }
  },
  "servers": [
    { "url": "https://api.screenata.com/api/audit", "description": "Production (preview — pilot access only)" }
  ],
  "security": [{ "AuditorToken": [] }],
  "tags": [
    { "name": "Cycles", "description": "Audit cycles the token's seat can access" },
    { "name": "Controls", "description": "Controls in scope for a cycle, with test-level statuses" },
    { "name": "Evidence", "description": "Evidence submissions and files, with hashes and downloads" },
    { "name": "Policies", "description": "Policies submitted to the auditor for a cycle" },
    { "name": "Packages", "description": "Prepared audit packages (signed evidence bundles)" }
  ],
  "paths": {
    "/v1/cycles": {
      "get": {
        "tags": ["Cycles"],
        "operationId": "listCycles",
        "summary": "List accessible audit cycles",
        "description": "Returns every audit cycle the token's auditor seat has been granted access to, across customer workspaces.",
        "responses": {
          "200": {
            "description": "Accessible cycles.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": ["schemaVersion", "generatedAt", "cycles"],
                          "properties": {
                            "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
                            "generatedAt": { "type": "string", "format": "date-time" },
                            "cycles": { "type": "array", "items": { "$ref": "#/components/schemas/Cycle" } }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/cycles/{cycleId}": {
      "get": {
        "tags": ["Cycles"],
        "operationId": "getCycle",
        "summary": "Cycle detail and status rollup",
        "description": "Cycle metadata plus a dashboard rollup: tests by state, evidence counts, and readiness.",
        "parameters": [{ "$ref": "#/components/parameters/cycleId" }],
        "responses": {
          "200": {
            "description": "Cycle detail.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/CycleDetail" } } }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/controls": {
      "get": {
        "tags": ["Controls"],
        "operationId": "listControls",
        "summary": "Controls tree with test statuses",
        "description": "The in-scope controls for the cycle, grouped by domain, each with its tests and their effective statuses. Control identity is the auditor-visible citation (e.g. `CC6.1`), never an internal id.",
        "parameters": [
          { "$ref": "#/components/parameters/cycleId" },
          { "name": "search", "in": "query", "schema": { "type": "string" }, "description": "Free-text filter over control refs and titles." }
        ],
        "responses": {
          "200": {
            "description": "Controls tree.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/ControlsTree" } } }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/evidence": {
      "get": {
        "tags": ["Evidence"],
        "operationId": "listEvidence",
        "summary": "List evidence submissions and files",
        "description": "Paginated evidence for the cycle — test submissions and their linked files — grouped by control. Use `cursor` from the previous response to page.",
        "parameters": [
          { "$ref": "#/components/parameters/cycleId" },
          { "name": "reviewStatus", "in": "query", "schema": { "type": "string", "enum": ["all", "pending", "accepted", "rejected", "flagged"] }, "description": "Filter by your review status." },
          { "name": "controlId", "in": "query", "schema": { "type": "string" }, "description": "Filter to one control citation (e.g. `CC6.1`)." },
          { "name": "kind", "in": "query", "schema": { "type": "string", "enum": ["all", "submission", "file"] } },
          { "name": "search", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } },
          { "name": "cursor", "in": "query", "schema": { "type": "string" }, "description": "Opaque cursor from `nextCursor`." }
        ],
        "responses": {
          "200": {
            "description": "Evidence page.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/EvidenceList" } } }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/evidence/{kind}/{itemId}": {
      "get": {
        "tags": ["Evidence"],
        "operationId": "getEvidenceItem",
        "summary": "One evidence item",
        "description": "A single submission or file, with linked files and content hashes.",
        "parameters": [
          { "$ref": "#/components/parameters/cycleId" },
          { "name": "kind", "in": "path", "required": true, "schema": { "type": "string", "enum": ["submission", "file"] } },
          { "name": "itemId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Evidence item.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/EvidenceItem" } } }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/evidence/file/{fileId}/content": {
      "get": {
        "tags": ["Evidence"],
        "operationId": "downloadEvidenceFile",
        "summary": "Download an evidence file",
        "description": "Streams the file bytes. The `X-Content-Sha256` response header matches the `contentSha256` from the listing, so the pull can be verified byte-for-byte.",
        "parameters": [
          { "$ref": "#/components/parameters/cycleId" },
          { "name": "fileId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "File bytes.",
            "headers": {
              "X-Content-Sha256": { "schema": { "type": "string" }, "description": "Hex SHA-256 of the response body." },
              "Content-Disposition": { "schema": { "type": "string" } }
            },
            "content": { "application/octet-stream": { "schema": { "type": "string", "format": "binary" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/cycles/{cycleId}/submissions/{submissionId}": {
      "get": {
        "tags": ["Evidence"],
        "operationId": "getSubmission",
        "summary": "Submission detail",
        "description": "One test submission with its review-status history and linked evidence files.",
        "parameters": [
          { "$ref": "#/components/parameters/cycleId" },
          { "name": "submissionId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Submission detail.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/SubmissionDetail" } } }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/policies": {
      "get": {
        "tags": ["Policies"],
        "operationId": "listPolicies",
        "summary": "List policies",
        "description": "Policies in scope for the cycle with their submission state.",
        "parameters": [{ "$ref": "#/components/parameters/cycleId" }],
        "responses": {
          "200": {
            "description": "Policies.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": ["schemaVersion", "generatedAt", "policies"],
                          "properties": {
                            "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
                            "generatedAt": { "type": "string", "format": "date-time" },
                            "policies": { "type": "array", "items": { "$ref": "#/components/schemas/Policy" } }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/policies/{policySlug}": {
      "get": {
        "tags": ["Policies"],
        "operationId": "getPolicy",
        "summary": "Policy detail",
        "description": "One policy with rendered content and submission/approval metadata.",
        "parameters": [
          { "$ref": "#/components/parameters/cycleId" },
          { "name": "policySlug", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Policy detail.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/PolicyDetail" } } }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/packages": {
      "get": {
        "tags": ["Packages"],
        "operationId": "listPackages",
        "summary": "List audit packages",
        "description": "Prepared audit packages (evidence bundles) for the cycle, with `manifestHash` for verification.",
        "parameters": [{ "$ref": "#/components/parameters/cycleId" }],
        "responses": {
          "200": {
            "description": "Packages.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "required": ["schemaVersion", "generatedAt", "packages"],
                          "properties": {
                            "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
                            "generatedAt": { "type": "string", "format": "date-time" },
                            "packages": { "type": "array", "items": { "$ref": "#/components/schemas/Package" } }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/cycles/{cycleId}/packages/{packageId}/content": {
      "get": {
        "tags": ["Packages"],
        "operationId": "downloadPackage",
        "summary": "Download an audit package",
        "description": "Streams the package zip. Verify against `manifestHash` from the listing.",
        "parameters": [
          { "$ref": "#/components/parameters/cycleId" },
          { "name": "packageId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Package zip.",
            "headers": {
              "X-Manifest-Sha256": { "schema": { "type": "string" }, "description": "The package's manifest hash." }
            },
            "content": { "application/zip": { "schema": { "type": "string", "format": "binary" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "AuditorToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "Personal auditor API token (`sk_audit_…`), minted from the Auditor Center. Bound to your auditor seat; per-cycle access is re-checked on every request."
      }
    },
    "parameters": {
      "cycleId": {
        "name": "cycleId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" },
        "description": "Audit cycle id, from `GET /v1/cycles`."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, expired, or revoked token.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "The token's seat has no access to this cycle.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found within the cycle.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Per-token rate limit exceeded.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "SchemaVersion": { "type": "string", "const": "audit-api/1.0" },
      "Envelope": {
        "type": "object",
        "required": ["success"],
        "properties": { "success": { "type": "boolean", "const": true } }
      },
      "Error": {
        "type": "object",
        "required": ["success", "error"],
        "properties": {
          "success": { "type": "boolean", "const": false },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "enum": ["NOT_FOUND", "FORBIDDEN", "UNAUTHORIZED", "BAD_REQUEST", "RATE_LIMITED", "CONFLICT", "INTERNAL_ERROR"] },
              "message": { "type": "string" }
            }
          }
        }
      },
      "Cycle": {
        "type": "object",
        "required": ["id", "label", "status"],
        "properties": {
          "id": { "type": "string" },
          "organizationName": { "type": ["string", "null"], "description": "The audited customer." },
          "label": { "type": "string", "examples": ["SOC 2 Type II FY26"] },
          "framework": { "type": ["string", "null"], "examples": ["SOC2", "HIPAA", "ISO27001"] },
          "auditType": { "type": ["string", "null"], "examples": ["Type I", "Type II"] },
          "periodStart": { "type": ["string", "null"], "format": "date" },
          "periodEnd": { "type": ["string", "null"], "format": "date" },
          "status": { "type": "string", "enum": ["active", "completed"] }
        }
      },
      "CycleDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/Cycle" },
          {
            "type": "object",
            "properties": {
              "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
              "generatedAt": { "type": "string", "format": "date-time" },
              "summary": {
                "type": "object",
                "description": "Dashboard rollup.",
                "properties": {
                  "controlsInScope": { "type": "integer" },
                  "testsByStatus": { "type": "object", "additionalProperties": { "type": "integer" } },
                  "evidenceSubmissions": { "type": "integer" },
                  "evidenceFiles": { "type": "integer" },
                  "policiesSubmitted": { "type": "integer" }
                }
              }
            }
          }
        ]
      },
      "ControlsTree": {
        "type": "object",
        "required": ["schemaVersion", "generatedAt", "domains"],
        "properties": {
          "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
          "generatedAt": { "type": "string", "format": "date-time" },
          "domains": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["domain", "controls"],
              "properties": {
                "domain": { "type": "string", "examples": ["Logical and Physical Access Controls"] },
                "controls": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": ["controlRef", "title", "tests"],
                    "properties": {
                      "controlRef": { "type": "string", "description": "Auditor-visible citation.", "examples": ["CC6.1"] },
                      "title": { "type": "string" },
                      "tests": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "required": ["id", "code", "title", "status"],
                          "properties": {
                            "id": { "type": "string" },
                            "code": { "type": "string", "examples": ["TEST-121"] },
                            "title": { "type": "string" },
                            "status": { "type": "string", "description": "Effective status of the test." },
                            "reviewStatus": { "type": ["string", "null"], "description": "Your latest review status, if any." }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "EvidenceItem": {
        "type": "object",
        "required": ["id", "kind", "title", "controlRef"],
        "properties": {
          "id": { "type": "string" },
          "kind": { "type": "string", "enum": ["submission", "file"] },
          "title": { "type": "string" },
          "controlRef": { "type": "string", "examples": ["CC6.1"] },
          "reviewStatus": { "type": ["string", "null"] },
          "submittedAt": { "type": ["string", "null"], "format": "date-time" },
          "uploadedAt": { "type": ["string", "null"], "format": "date-time" },
          "fileType": { "type": ["string", "null"], "examples": ["application/pdf"] },
          "sizeBytes": { "type": ["integer", "null"] },
          "contentSha256": { "type": ["string", "null"], "description": "Files only. Verify downloads against this." },
          "content": { "type": ["string", "null"], "format": "uri", "description": "Absolute URL of the authenticated `/content` endpoint. Files only." },
          "linkedFiles": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["id", "title", "content"],
              "properties": {
                "id": { "type": "string" },
                "title": { "type": "string" },
                "contentSha256": { "type": ["string", "null"] },
                "content": { "type": "string", "format": "uri" }
              }
            }
          }
        }
      },
      "EvidenceList": {
        "type": "object",
        "required": ["schemaVersion", "generatedAt", "groups", "totalCount", "nextCursor"],
        "properties": {
          "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
          "generatedAt": { "type": "string", "format": "date-time" },
          "groups": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["controlRef", "controlTitle", "items"],
              "properties": {
                "controlRef": { "type": "string" },
                "controlTitle": { "type": "string" },
                "items": { "type": "array", "items": { "$ref": "#/components/schemas/EvidenceItem" } }
              }
            }
          },
          "totalCount": { "type": "integer" },
          "nextCursor": { "type": ["string", "null"], "description": "Pass as `cursor` to fetch the next page; null on the last page." }
        }
      },
      "SubmissionDetail": {
        "type": "object",
        "required": ["id", "controlRef", "reviewStatus", "submittedAt"],
        "properties": {
          "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
          "id": { "type": "string" },
          "testCode": { "type": ["string", "null"], "examples": ["TEST-121"] },
          "testTitle": { "type": ["string", "null"] },
          "controlRef": { "type": "string" },
          "reviewStatus": { "type": ["string", "null"] },
          "submittedAt": { "type": "string", "format": "date-time" },
          "reviewHistory": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "status": { "type": "string" },
                "at": { "type": "string", "format": "date-time" }
              }
            }
          },
          "files": { "type": "array", "items": { "$ref": "#/components/schemas/EvidenceItem" } }
        }
      },
      "Policy": {
        "type": "object",
        "required": ["slug", "title", "submissionStatus"],
        "properties": {
          "slug": { "type": "string", "examples": ["access-control-policy"] },
          "title": { "type": "string", "examples": ["Access Control Policy"] },
          "version": { "type": ["string", "null"] },
          "submissionStatus": { "type": "string", "description": "Whether and when it was submitted to you for review." },
          "submittedAt": { "type": ["string", "null"], "format": "date-time" },
          "approvedAt": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "PolicyDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/Policy" },
          {
            "type": "object",
            "properties": {
              "schemaVersion": { "$ref": "#/components/schemas/SchemaVersion" },
              "contentHtml": { "type": ["string", "null"], "description": "Rendered policy content." },
              "contentSha256": { "type": ["string", "null"] }
            }
          }
        ]
      },
      "Package": {
        "type": "object",
        "required": ["id", "status", "createdAt"],
        "properties": {
          "id": { "type": "string" },
          "status": { "type": "string" },
          "fileName": { "type": ["string", "null"] },
          "fileSize": { "type": ["integer", "null"] },
          "artifactCount": { "type": ["integer", "null"] },
          "manifestHash": { "type": ["string", "null"], "description": "Hash over the package manifest; verify the downloaded zip against it." },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      }
    }
  }
}
