{"components":{"schemas":{"AssignProjectEntry":{"additionalProperties":false,"properties":{"classId":{"description":"Optional class to grant on this project (must be declared in the project's spec).","type":"string"},"evidenceNote":{"description":"Why the validator qualifies for classId; required when classId is set.","type":"string"},"id":{"description":"Project ID.","format":"uuid","type":"string"}},"required":["id"],"type":"object"},"AssignValidatorInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/AssignValidatorInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"autoEnroll":{"description":"When true, promote the user to an org validator (is_validator) before assignment if they are an existing org member but not yet flagged a validator. The user must have a user row; an unknown validatorId still returns 404 validator_not_found.","type":"boolean"},"projects":{"description":"Projects in the API key's organization to assign this validator to, each optionally granting a class.","items":{"$ref":"#/components/schemas/AssignProjectEntry"},"minItems":1,"type":["array","null"]}},"required":["projects"],"type":"object"},"AssignValidatorOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/AssignValidatorOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"alreadyAssigned":{"description":"Number of projects the validator was already assigned to.","format":"int64","type":"integer"},"assigned":{"description":"Number of projects the validator was newly assigned to.","format":"int64","type":"integer"},"classesGranted":{"description":"Number of class grants applied across the assigned projects.","format":"int64","type":"integer"}},"required":["assigned","alreadyAssigned","classesGranted"],"type":"object"},"CancelProjectInviteOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/CancelProjectInviteOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"cancelled":{"description":"Always true on success; cancellation is idempotent for a pending invite.","type":"boolean"}},"required":["cancelled"],"type":"object"},"CreateProjectInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/CreateProjectInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"name":{"description":"Human-readable project name.","maxLength":200,"minLength":1,"type":"string"},"spec":{"description":"Project specification as a JSON object (1:1 mirror of the poq.toml spec).","type":"object"}},"required":["name","spec"],"type":"object"},"CreateProjectInviteInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/CreateProjectInviteInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"classId":{"description":"Optional class to pre-stage on the invite (fulfilled at login acceptance). Must be declared in the project's spec.","type":"string"},"email":{"description":"Email address to invite (case-insensitive; normalized on write).","minLength":1,"type":"string"},"evidenceNote":{"description":"Why the invitee qualifies for classId; required when classId is set.","type":"string"},"sendEmail":{"description":"Send the invite email (ProjectInvite template). Defaults to true; set to false to skip the email (e.g. for testing or custom delivery).","type":"boolean"}},"required":["email"],"type":"object"},"CreateProjectOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/CreateProjectOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"projectId":{"description":"ID of the newly created project.","type":"string"},"specId":{"description":"ID of the project's first compiled spec.","type":"string"},"specVersion":{"description":"Spec version parsed from the submitted spec.","type":"string"},"status":{"description":"Lifecycle status of the new project (always \"active\").","type":"string"}},"required":["projectId","specId","specVersion","status"],"type":"object"},"CreateUploadSessionInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/CreateUploadSessionInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"files":{"description":"Files this session will contain. More files can be declared later via the declare-files endpoint.","items":{"$ref":"#/components/schemas/DeclaredUploadFile"},"maxItems":1000,"minItems":1,"type":["array","null"]}},"required":["files"],"type":"object"},"CreateUploadSessionOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/CreateUploadSessionOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"expiresAt":{"description":"RFC 3339 time the presigned URLs stop working. The session itself does not expire — request fresh URLs via the declare-files endpoint.","type":"string"},"files":{"description":"One presigned PUT URL per declared file.","items":{"$ref":"#/components/schemas/PresignedUploadResource"},"type":["array","null"]},"keyPrefix":{"description":"Server-owned object-storage folder all session files live under.","type":"string"},"uploadId":{"description":"ID of the new upload session. Pass it to the declare-files, finalize, and ingest endpoints.","type":"string"}},"required":["uploadId","keyPrefix","files","expiresAt"],"type":"object"},"DatapointConsensusResource":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/DatapointConsensusResource.json"],"format":"uri","readOnly":true,"type":"string"},"consensusDecisionId":{"description":"ID of the terminal consensus decision (matches the datapoint.consensus_reached webhook).","type":"string"},"consensusStrength":{"description":"Consensus Strength for the datapoint.","format":"double","type":"number"},"datapointId":{"description":"Datapoint ID.","type":"string"},"projectId":{"description":"ID of the datapoint's project.","type":"string"},"qualityRatingMean":{"description":"Directional Quality Rating (0-100), consensus-weighted mean.","format":"double","type":"number"},"qualityRatingMedian":{"description":"Directional Quality Rating (0-100), consensus-weighted median.","format":"double","type":"number"}},"required":["datapointId","projectId","consensusDecisionId","qualityRatingMean","qualityRatingMedian","consensusStrength"],"type":"object"},"DeclareUploadFilesInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/DeclareUploadFilesInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"files":{"description":"Files to declare (new paths are added to the session; already-declared paths get fresh URLs and their declared size is updated).","items":{"$ref":"#/components/schemas/DeclaredUploadFile"},"maxItems":1000,"minItems":1,"type":["array","null"]}},"required":["files"],"type":"object"},"DeclareUploadFilesOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/DeclareUploadFilesOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"expiresAt":{"description":"RFC 3339 time these presigned URLs stop working. Call this endpoint again for fresh ones.","type":"string"},"files":{"description":"One presigned PUT URL per file in this request.","items":{"$ref":"#/components/schemas/PresignedUploadResource"},"type":["array","null"]},"keyPrefix":{"description":"Server-owned object-storage folder all session files live under.","type":"string"}},"required":["keyPrefix","files","expiresAt"],"type":"object"},"DeclaredFileResource":{"additionalProperties":false,"properties":{"relPath":{"description":"File path relative to the session folder.","type":"string"},"size":{"description":"Declared size in bytes — the per-file upload cap enforced when the session is processed.","format":"int64","type":"integer"}},"required":["relPath","size"],"type":"object"},"DeclaredUploadFile":{"additionalProperties":false,"properties":{"relPath":{"description":"File path relative to the session folder, forward slashes, no leading slash and no \"..\" (e.g. \"data/rows.csv\").","maxLength":900,"minLength":1,"type":"string"},"size":{"description":"Exact file size in bytes (max 5 GiB). Finalize verifies the uploaded object against this size, so declare the real size.","format":"int64","maximum":5368709120,"minimum":0,"type":"integer"}},"required":["relPath","size"],"type":"object"},"DeveloperStatusError":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/DeveloperStatusError.json"],"format":"uri","readOnly":true,"type":"string"},"error":{"$ref":"#/components/schemas/ErrorEnvelope"},"requestId":{"type":"string"}},"required":["error"],"type":"object"},"ErrorEnvelope":{"additionalProperties":false,"properties":{"code":{"type":"string"},"message":{"type":"string"},"param":{"type":"string"},"type":{"type":"string"}},"required":["type","code","message"],"type":"object"},"GetUploadSessionOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/GetUploadSessionOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"createdAt":{"description":"RFC 3339 creation time.","type":"string"},"files":{"description":"Every file declared on the session so far.","items":{"$ref":"#/components/schemas/DeclaredFileResource"},"type":["array","null"]},"keyPrefix":{"description":"Server-owned object-storage folder all session files live under.","type":"string"},"status":{"description":"Session lifecycle: open (accepting files), processing (ingest run in flight), complete, or failed (call process again to retry).","type":"string"},"updatedAt":{"description":"RFC 3339 last-update time.","type":"string"},"uploadId":{"description":"Upload session ID.","type":"string"},"verifiedBytes":{"description":"Total verified bytes across those files.","format":"int64","type":"integer"},"verifiedFileCount":{"description":"Number of declared files verified present in object storage by the most recent process call; 0 until then.","format":"int32","type":"integer"}},"required":["uploadId","status","keyPrefix","files","verifiedFileCount","verifiedBytes","createdAt","updatedAt"],"type":"object"},"GetUploadSessionStatusOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/GetUploadSessionStatusOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"run":{"$ref":"#/components/schemas/IngestRunResource","description":"Most recent ingest run for this session; null before the first process call."},"sessionStatus":{"description":"Session lifecycle: open, processing, complete, or failed.","type":"string"},"uploadId":{"description":"Upload session ID.","type":"string"}},"required":["uploadId","sessionStatus","run"],"type":"object"},"GrantValidatorClassInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/GrantValidatorClassInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"classId":{"description":"Class ID declared in the project's spec (see GET /projects/{projectId}/classes).","minLength":1,"type":"string"},"evidenceNote":{"description":"Why this validator qualifies for the class (recorded for audit).","minLength":1,"type":"string"}},"required":["classId","evidenceNote"],"type":"object"},"IngestRunResource":{"additionalProperties":false,"properties":{"datapointsPersisted":{"description":"Datapoints written so far (0 for dry runs).","format":"int32","type":"integer"},"errorMessage":{"description":"Populated when the run failed; explains what went wrong.","type":"string"},"finishedAt":{"description":"RFC 3339 completion time; null while the run is still going.","type":["string","null"]},"phase":{"description":"Progress within a running run: queued → reading → writing_datapoints.","type":"string"},"rowCount":{"description":"Rows read from the uploaded data so far.","format":"int32","type":"integer"},"runId":{"description":"Ingest run ID.","type":"string"},"startedAt":{"description":"RFC 3339 run start time.","type":"string"},"status":{"description":"Run outcome: running, ok, or failed.","type":"string"},"updatedAt":{"description":"RFC 3339 last progress update.","type":"string"},"validationsCreated":{"description":"Validation slots created so far (0 for dry runs).","format":"int32","type":"integer"}},"required":["runId","status","phase","rowCount","datapointsPersisted","validationsCreated","startedAt","updatedAt","finishedAt"],"type":"object"},"ListProjectClassesOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ListProjectClassesOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"data":{"description":"Validator classes declared in the project's spec.","items":{"$ref":"#/components/schemas/ValidatorClassResource"},"type":["array","null"]}},"required":["data"],"type":"object"},"ListProjectInvitesOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ListProjectInvitesOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"data":{"description":"The project's non-fulfilled invites (pending + cancelled), newest first.","items":{"$ref":"#/components/schemas/ProjectInviteResource"},"type":["array","null"]},"hasMore":{"description":"True when more invites are available after this page.","type":"boolean"},"nextCursor":{"description":"Cursor to pass as ?cursor= for the next page; null on the last page.","type":["string","null"]}},"required":["data","hasMore","nextCursor"],"type":"object"},"ListProjectsOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ListProjectsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"data":{"description":"Projects in the API key's organization, newest first.","items":{"$ref":"#/components/schemas/ProjectResource"},"type":["array","null"]},"hasMore":{"description":"True when more projects are available after this page.","type":"boolean"},"nextCursor":{"description":"Cursor to pass as ?cursor= for the next page; null on the last page.","type":["string","null"]}},"required":["data","hasMore","nextCursor"],"type":"object"},"ListValidatorProjectsOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ListValidatorProjectsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"data":{"description":"The validator's non-revoked project assignments in the API key's organization.","items":{"$ref":"#/components/schemas/ValidatorAssignmentResource"},"type":["array","null"]}},"required":["data"],"type":"object"},"ListValidatorsOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ListValidatorsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"data":{"description":"The org validator roster (list mode); null in lookup mode.","items":{"$ref":"#/components/schemas/ValidatorResource"},"type":["array","null"]},"hasMore":{"description":"True when more validators are available after this page (list mode).","type":"boolean"},"nextCursor":{"description":"Cursor to pass as ?cursor= for the next page (list mode); null on the last page or in lookup mode.","type":["string","null"]},"validator":{"$ref":"#/components/schemas/ValidatorResource","description":"The single org validator matching email (lookup mode); null in list mode."}},"required":["validator","data","hasMore","nextCursor"],"type":"object"},"PingOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/PingOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"pong":{"description":"Always true when the API key is valid.","type":"boolean"}},"required":["pong"],"type":"object"},"PresignedUploadResource":{"additionalProperties":false,"properties":{"key":{"description":"Full object-storage key the file will land at (session prefix + relPath).","type":"string"},"relPath":{"description":"The file's path relative to the session folder, exactly as declared.","type":"string"},"uploadUrl":{"description":"Presigned PUT URL. Upload the file bytes directly to this URL with an HTTP PUT; no Authorization header is needed (or allowed) on that request.","type":"string"}},"required":["relPath","key","uploadUrl"],"type":"object"},"ProcessUploadSessionInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ProcessUploadSessionInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"dryRun":{"description":"Run the full ingest pipeline without persisting datapoints or validations. Defaults to false.","type":"boolean"},"skipExisting":{"description":"Skip datapoints whose checksum already exists for the project instead of failing on them. Defaults to false.","type":"boolean"}},"type":"object"},"ProcessUploadSessionOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ProcessUploadSessionOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"dryRun":{"description":"Whether this run persists nothing.","type":"boolean"},"phase":{"description":"Initial run phase (always \"queued\").","type":"string"},"runId":{"description":"ID of the ingest run just started. Poll the status endpoint to follow it.","type":"string"},"skipExisting":{"description":"Whether duplicate datapoints are skipped.","type":"boolean"},"status":{"description":"Initial run status (always \"running\").","type":"string"}},"required":["runId","status","phase","dryRun","skipExisting"],"type":"object"},"ProjectInviteResource":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ProjectInviteResource.json"],"format":"uri","readOnly":true,"type":"string"},"activeClassIds":{"description":"Class ids pre-staged on the invite (fulfilled at login acceptance); empty when none.","items":{"type":"string"},"type":["array","null"]},"assignedAt":{"description":"RFC3339 time the invite was fulfilled (login acceptance); null when pending or cancelled.","type":["string","null"]},"cancelledAt":{"description":"RFC3339 time the invite was soft-cancelled; null when pending or accepted.","type":["string","null"]},"createdAt":{"description":"RFC3339 time the invite was created.","type":"string"},"email":{"description":"Invited email address.","type":"string"},"emailSentAt":{"description":"RFC3339 time the invite email was last sent; null when not yet sent.","type":["string","null"]},"id":{"description":"Invite ID.","type":"string"},"invitedByEmail":{"description":"Email of the originator who created the invite; empty for admin-created invites.","type":"string"},"projectId":{"description":"Project the invite is for.","type":"string"},"status":{"description":"Invite status: \"pending\", \"accepted\", or \"cancelled\".","type":"string"}},"required":["id","email","projectId","status","assignedAt","cancelledAt","invitedByEmail","emailSentAt","createdAt","activeClassIds"],"type":"object"},"ProjectResource":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ProjectResource.json"],"format":"uri","readOnly":true,"type":"string"},"archived":{"description":"True when the project is archived (hidden from validators, no new claims).","type":"boolean"},"archivedAt":{"description":"RFC3339 time the project was archived; null when not archived.","type":["string","null"]},"autoConsensus":{"description":"True when Auto Consensus test mode is enabled (synthetic validators auto-complete validations; testing only).","type":"boolean"},"autoConsensusSpread":{"description":"Auto Consensus vote spread: \"tight\", \"medium\", or \"wild\".","type":"string"},"autoConsensusUsed":{"description":"True once synthetic (Auto Consensus) votes have ever been submitted for this project; sticky, so the project's outcomes are flagged as test-contaminated even after Auto Consensus is disabled.","type":"boolean"},"createdAt":{"description":"RFC3339 creation time.","type":"string"},"description":{"description":"Project description; null when unset.","type":["string","null"]},"id":{"description":"Project ID.","type":"string"},"name":{"description":"Human-readable project name.","type":"string"},"numValidators":{"description":"Validators required per datapoint.","format":"int32","type":"integer"},"paused":{"description":"True when new validation claims are paused.","type":"boolean"},"pausedAt":{"description":"RFC3339 time the project was paused; null when not paused.","type":["string","null"]},"status":{"description":"Lifecycle status (\"draft\" or \"active\").","type":"string"},"updatedAt":{"description":"RFC3339 last-update time.","type":"string"}},"required":["id","name","description","status","paused","pausedAt","archived","archivedAt","autoConsensus","autoConsensusSpread","autoConsensusUsed","numValidators","createdAt","updatedAt"],"type":"object"},"ProofReportResource":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ProofReportResource.json"],"format":"uri","readOnly":true,"type":"string"},"attestationRoot":{"description":"sha256:\u003chex\u003e Merkle root over config + manifest + results hashes.","type":"string"},"consensusStrength":{"description":"Headline Consensus Strength (0-100), averaged across the covered datapoints.","format":"int64","type":"integer"},"issuedAt":{"description":"RFC3339 issuance time (canonical UTC).","type":"string"},"jws":{"description":"Compact JWS (Ed25519) signing the canonical payload; verify with the published JWKS.","type":"string"},"payload":{"description":"Canonical payload (RFC 8785 JCS JSON) — the full signed Proof Report record.","type":"object"},"qualityRating":{"description":"Headline Quality Rating (0-100), averaged across the covered datapoints.","format":"int64","type":"integer"},"reportId":{"description":"Report ID (PR-\u003cyyyy\u003e-\u003cmmdd\u003e-\u003cORG\u003e-\u003cseq4\u003e).","type":"string"},"schema":{"description":"Payload schema identifier (poq.attestation/v1).","type":"string"},"signingKeyId":{"description":"ID of the Sapien signing key (matches the kid in the published JWKS).","type":"string"}},"required":["reportId","schema","issuedAt","qualityRating","consensusStrength","attestationRoot","signingKeyId","payload","jws"],"type":"object"},"ReportExportCoverage":{"additionalProperties":false,"properties":{"datapointsFinalized":{"description":"Datapoints with terminal consensus (the report's coverage).","format":"int64","type":"integer"},"datapointsTotal":{"description":"Total datapoints in the project.","format":"int64","type":"integer"},"partial":{"description":"True when the report covers only part of the dataset.","type":"boolean"}},"required":["datapointsFinalized","datapointsTotal","partial"],"type":"object"},"ReportExportResource":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ReportExportResource.json"],"format":"uri","readOnly":true,"type":"string"},"contentType":{"description":"MIME type of the stored artifact the URL serves.","type":"string"},"coverage":{"$ref":"#/components/schemas/ReportExportCoverage","description":"How much of the project's dataset the report covers."},"expiresAt":{"description":"RFC3339 UTC expiry of the presigned URL.","type":"string"},"filename":{"description":"Suggested download filename (poq-report-\u003cprojectId\u003e-\u003cdate\u003e.\u003cext\u003e).","type":"string"},"format":{"description":"The requested export format.","type":"string"},"url":{"description":"Presigned GET URL for the artifact. Expires at expiresAt.","type":"string"}},"required":["format","contentType","filename","url","expiresAt","coverage"],"type":"object"},"RevokeValidatorClassOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/RevokeValidatorClassOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"revoked":{"description":"Always true; revocation is idempotent.","type":"boolean"}},"required":["revoked"],"type":"object"},"UnassignValidatorInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/UnassignValidatorInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"projectIds":{"description":"IDs of projects in the API key's organization to unassign this validator from.","items":{"type":"string"},"minItems":1,"type":["array","null"]}},"required":["projectIds"],"type":"object"},"UnassignValidatorOutputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/UnassignValidatorOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"unassigned":{"description":"Number of project assignments processed (revoke is idempotent, so an already-unassigned project still counts).","format":"int64","type":"integer"}},"required":["unassigned"],"type":"object"},"UpdateProjectInputBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/UpdateProjectInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"archived":{"description":"Set true to archive the project (hidden from validators, no new claims), false to unarchive.","type":"boolean"},"autoConsensus":{"description":"Set true to enable Auto Consensus test mode (synthetic validators auto-complete validations), false to disable.","type":"boolean"},"autoConsensusSpread":{"description":"Auto Consensus vote spread: tight (unanimous), medium (realistic), or wild (contested, may escalate).","enum":["tight","medium","wild"],"type":"string"},"description":{"description":"New project description; pass an empty string to clear it.","maxLength":2000,"type":"string"},"name":{"description":"New project name.","maxLength":200,"minLength":1,"type":"string"},"paused":{"description":"Set true to pause new validation claims, false to resume.","type":"boolean"}},"type":"object"},"ValidatorAssignmentResource":{"additionalProperties":false,"properties":{"assignedAt":{"description":"RFC3339 time the validator was assigned to the project.","type":"string"},"assignmentId":{"description":"ID of the validator↔project assignment.","type":"string"},"projectId":{"description":"ID of the assigned project.","type":"string"},"projectName":{"description":"Name of the assigned project.","type":"string"}},"required":["assignmentId","projectId","projectName","assignedAt"],"type":"object"},"ValidatorClassGrantResource":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/developer/v1/ValidatorClassGrantResource.json"],"format":"uri","readOnly":true,"type":"string"},"classId":{"description":"Granted class ID.","type":"string"},"evidenceNote":{"description":"Recorded qualification justification.","type":"string"},"id":{"description":"Class-grant ID.","type":"string"},"userId":{"description":"Validator (user) ID.","type":"string"}},"required":["id","userId","classId","evidenceNote"],"type":"object"},"ValidatorClassResource":{"additionalProperties":false,"properties":{"consensusWeight":{"description":"Relative weight of this class's votes in consensus.","format":"double","type":"number"},"id":{"description":"Class ID (referenced when granting a class).","type":"string"},"label":{"description":"Human-readable class label.","type":"string"},"model":{"description":"AI model backing the class; null for human classes.","type":["string","null"]},"rewardCents":{"description":"Reward per validation for this class, in cents.","format":"int64","type":"integer"},"stakeWei":{"description":"Stake required per validation for this class, in wei.","type":"string"},"type":{"description":"Class type: \"human\" or \"ai\".","type":"string"}},"required":["id","label","type","rewardCents","stakeWei","consensusWeight","model"],"type":"object"},"ValidatorResource":{"additionalProperties":false,"properties":{"email":{"description":"Validator email; empty when the user has none on file.","type":"string"},"evmAddress":{"description":"Validator EVM address; empty when the user has none on file.","type":"string"},"isOriginator":{"description":"True when the user is an originator of the organization.","type":"boolean"},"isValidator":{"description":"True when the user is a validator of the organization (is_validator flag or project assignment).","type":"boolean"},"joinedAt":{"description":"RFC3339 time the user became an org validator (org membership created_at, or first project assignment).","type":"string"},"subject":{"description":"Validator subject (auth identifier).","type":"string"},"userId":{"description":"Validator (user) ID.","type":"string"}},"required":["userId","subject","email","evmAddress","isOriginator","isValidator","joinedAt"],"type":"object"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"API key","description":"Developer API key using the Authorization: Bearer poq_live_... header.","scheme":"bearer","type":"http"}}},"info":{"description":"External developer API authenticated with API keys.","title":"Sapien Developer API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/datapoints/{datapointId}/consensus":{"get":{"description":"Fetch the terminal consensus outcome for a datapoint in the API key's organization: the headline quality rating and consensus strength, plus the consensusDecisionId. Resolves a datapoint.consensus_reached webhook back to its outcome. Returns 409 consensus_not_finalized when the datapoint exists but has not reached terminal consensus yet, and 404 datapoint_not_found when there is no such datapoint in the organization.","operationId":"getDatapointConsensus","parameters":[{"description":"Datapoint ID.","in":"path","name":"datapointId","required":true,"schema":{"description":"Datapoint ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DatapointConsensusResource"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Get datapoint consensus","tags":["Datapoints"]}},"/ping":{"get":{"description":"Checks API key authentication and developer API availability.","operationId":"ping","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Ping developer API","tags":["System"]}},"/projects":{"get":{"description":"List the projects in the API key's organization, newest first. Cursor-paginated: pass the previous response's nextCursor as ?cursor= to fetch the next page.","operationId":"listProjects","parameters":[{"description":"Maximum number of projects to return (1-100).","explode":false,"in":"query","name":"limit","schema":{"default":20,"description":"Maximum number of projects to return (1-100).","format":"int64","maximum":100,"minimum":1,"type":"integer"}},{"description":"Opaque pagination cursor from a previous response's nextCursor. Omit for the first page.","explode":false,"in":"query","name":"cursor","schema":{"description":"Opaque pagination cursor from a previous response's nextCursor. Omit for the first page.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectsOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"List projects","tags":["Projects"]},"post":{"description":"Create an active project from a JSON spec. The project goes live immediately (spec locked), is attributed to the user who created the API key, and is created in that user's organization.","operationId":"createProject","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectOutputBody"}}},"description":"Created"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Create a project","tags":["Projects"]}},"/projects/{projectId}":{"get":{"description":"Fetch a single project in the API key's organization by ID.","operationId":"getProject","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResource"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Get a project","tags":["Projects"]},"put":{"description":"Update a project's editable fields. Only the supplied fields change: name and description are mutable metadata; paused toggles whether new validation claims are issued. The spec, status, and other fields are immutable once the project is live.","operationId":"updateProject","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResource"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Update a project","tags":["Projects"]}},"/projects/{projectId}/classes":{"get":{"description":"List the validator classes declared in a project's spec ([validators].classes). These class ids are what you grant to a validator (standalone, or via the assign endpoint).","operationId":"listProjectClasses","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectClassesOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"List a project's validator classes","tags":["Validators"]}},"/projects/{projectId}/invites":{"get":{"description":"List the project's non-fulfilled invites (pending + cancelled), cursor-paginated, newest first. Accepted invitees are project validators and are visible via GET /validators, not this endpoint. Each row carries a status field (\"pending\" or \"cancelled\").","operationId":"listProjectInvites","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Maximum number of invites to return (1-100).","explode":false,"in":"query","name":"limit","schema":{"default":20,"description":"Maximum number of invites to return (1-100).","format":"int64","maximum":100,"minimum":1,"type":"integer"}},{"description":"Opaque pagination cursor from a previous response's nextCursor. Omit for the first page.","explode":false,"in":"query","name":"cursor","schema":{"description":"Opaque pagination cursor from a previous response's nextCursor. Omit for the first page.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectInvitesOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"List a project's invites","tags":["Validators"]},"post":{"description":"Create a pending invite for an email to join a project as a validator. Idempotent upsert keyed by (lower(email), project_id): a pending invite is returned unchanged, a cancelled one is reactivated, an already-accepted one is returned with status \"accepted\" and no email is sent. Optional classId + evidenceNote pre-stage a class grant on the invite (fulfilled at login acceptance); the class must be declared in the project's spec. The invite email is sent via the ProjectInvite template unless sendEmail is false.","operationId":"createProjectInvite","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectInviteInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInviteResource"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Invite a validator by email","tags":["Validators"]}},"/projects/{projectId}/invites/{inviteId}":{"delete":{"description":"Soft-cancel a pending invite (stamps cancelled_at; the row is preserved for audit). Idempotent on a pending invite. An already-cancelled invite returns 404 invite_not_found (it has left the pending list); an already-accepted invite returns 409 invite_already_accepted (cancelling would erase the audit trail of an active project_validator membership); a cross-project invite returns 404 invite_not_found.","operationId":"cancelProjectInvite","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Invite ID.","in":"path","name":"inviteId","required":true,"schema":{"description":"Invite ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelProjectInviteOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Cancel a pending invite","tags":["Validators"]}},"/projects/{projectId}/proof-report":{"get":{"description":"Issue and return the project's signed poq.attestation/v1 Proof Report — the two headline ratings (Quality Rating + Consensus Strength), the canonical payload, and the attestation root. The report is built on demand from the project's finalized consensus data and signed with the deployment's Ed25519 key; verify it offline against the published JWKS. Returns proof_report_not_ready when the project's dataset has not finished draining.","operationId":"getProjectProofReport","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProofReportResource"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Get the project's Proof Report","tags":["Projects"]}},"/projects/{projectId}/report":{"get":{"description":"Generate the project's PoQ report in any of the eight formats the portal's Download menu offers (JSON, CSV, signed attestation YAML, the verifiable Proof Report as text or HTML, and the report Markdown alone or bundled with the signed attestation as Markdown or ZIP), store the artifact, and return a short-lived presigned download URL plus dataset coverage. The report is built on demand from the project's finalized consensus data. Returns report_not_ready when nothing has finalized yet.","operationId":"getProjectReportExport","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Export format, one per portal Download menu item. json = full payload as raw JSON; csv = per-datapoint scores; attestation_yaml = signed attestation YAML; verifiable_txt / verifiable_html = the readable Proof Report with an embedded signature; report_md / report_attestation_md / report_attestation_zip = the report Markdown, optionally with the signed attestation appended or zipped alongside.","explode":false,"in":"query","name":"format","required":true,"schema":{"description":"Export format, one per portal Download menu item. json = full payload as raw JSON; csv = per-datapoint scores; attestation_yaml = signed attestation YAML; verifiable_txt / verifiable_html = the readable Proof Report with an embedded signature; report_md / report_attestation_md / report_attestation_zip = the report Markdown, optionally with the signed attestation appended or zipped alongside.","enum":["json","csv","attestation_yaml","verifiable_txt","verifiable_html","report_md","report_attestation_md","report_attestation_zip"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportExportResource"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Export the project's PoQ report in a chosen format","tags":["Projects"]}},"/projects/{projectId}/upload-sessions":{"post":{"description":"Open an upload session for a project and receive one presigned PUT URL per declared file. Upload each file's bytes directly to its uploadUrl with an HTTP PUT — do not send file bytes to this API. URLs expire after one hour; request fresh ones with the declare-files endpoint. The session stays open until an ingest run is started against it.","operationId":"createUploadSession","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadSessionInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadSessionOutputBody"}}},"description":"Created"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Create an upload session","tags":["Upload Sessions"]}},"/projects/{projectId}/upload-sessions/{uploadId}":{"get":{"description":"Fetch one upload session: its lifecycle status, the full list of declared files, and the verification counters recorded by the most recent process call.","operationId":"getUploadSession","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Upload session ID.","in":"path","name":"uploadId","required":true,"schema":{"description":"Upload session ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetUploadSessionOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Get an upload session","tags":["Upload Sessions"]}},"/projects/{projectId}/upload-sessions/{uploadId}/file":{"put":{"description":"Convenience variant of declare-files for single files: PUT the file body to this endpoint with ?path= and it responds 307 Temporary Redirect to the file's presigned URL before reading the body. The file size is taken from the request's Content-Length header automatically; pass ?size= only for chunked/streamed requests that carry no Content-Length. Requires an HTTP client that follows 307 redirects with method and body preserved and that strips the Authorization header on redirect (curl does: `curl -T file 'https://.../file?path=data.csv' -H 'Authorization: Bearer ...' --location`). Prefer the two-step declare-files flow for large files or when unsure of client redirect behavior.","operationId":"uploadFileRedirect","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Upload session ID.","in":"path","name":"uploadId","required":true,"schema":{"description":"Upload session ID.","format":"uuid","type":"string"}},{"description":"File path relative to the session folder (e.g. \"data/rows.csv\").","explode":false,"in":"query","name":"path","required":true,"schema":{"description":"File path relative to the session folder (e.g. \"data/rows.csv\").","maxLength":900,"minLength":1,"type":"string"}},{"description":"File size in bytes (max 5 GiB). Optional: defaults to the request's Content-Length, which clients like curl set automatically. Required only when the request is sent chunked (no Content-Length header).","explode":false,"in":"query","name":"size","schema":{"description":"File size in bytes (max 5 GiB). Optional: defaults to the request's Content-Length, which clients like curl set automatically. Required only when the request is sent chunked (no Content-Length header).","format":"int64","maximum":5368709120,"minimum":0,"type":"integer"}}],"responses":{"307":{"description":"Temporary Redirect","headers":{"Location":{"schema":{"description":"Presigned PUT URL for the file. Re-send the same PUT (with body) to this URL.","type":"string"}}}},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"One-shot file upload (307 redirect)","tags":["Upload Sessions"]}},"/projects/{projectId}/upload-sessions/{uploadId}/files":{"post":{"description":"Declare additional files on an open upload session, or get fresh presigned PUT URLs for files already declared (re-declaring a path updates its declared size and returns a new URL). The session's file list is merged, never replaced.","operationId":"declareUploadFiles","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Upload session ID.","in":"path","name":"uploadId","required":true,"schema":{"description":"Upload session ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclareUploadFilesInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclareUploadFilesOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Declare files / refresh upload URLs","tags":["Upload Sessions"]}},"/projects/{projectId}/upload-sessions/{uploadId}/process":{"post":{"description":"Verify the uploads and start the asynchronous ingest run in one call. Every declared file must already be uploaded (and within its declared size) or the call fails with the missing paths listed. Responds 202 with a runId; poll the status endpoint to follow the run. A failed session can be processed again to retry.","operationId":"processUploadSession","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Upload session ID.","in":"path","name":"uploadId","required":true,"schema":{"description":"Upload session ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessUploadSessionInputBody","description":"Optional processing options; omit the body for a plain run."}}}},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessUploadSessionOutputBody"}}},"description":"Accepted"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Process an upload session","tags":["Upload Sessions"]}},"/projects/{projectId}/upload-sessions/{uploadId}/status":{"get":{"description":"Poll the session's lifecycle state and its most recent ingest run: live progress counters while running, final counts when complete, and the error message when failed.","operationId":"getUploadSessionStatus","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Upload session ID.","in":"path","name":"uploadId","required":true,"schema":{"description":"Upload session ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetUploadSessionStatusOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Get processing status","tags":["Upload Sessions"]}},"/projects/{projectId}/validators/{validatorId}/classes":{"post":{"description":"Grant a spec-declared class to a validator on a project. Exclusive: any other active class the validator holds on the project is revoked first. The class must be declared in the project's spec.","operationId":"grantValidatorClass","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Validator (user) ID.","in":"path","name":"validatorId","required":true,"schema":{"description":"Validator (user) ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantValidatorClassInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidatorClassGrantResource"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Grant a validator a class on a project","tags":["Validators"]}},"/projects/{projectId}/validators/{validatorId}/classes/{classId}":{"delete":{"description":"Soft-revoke a validator's class grant on a project. Idempotent: revoking a class the validator does not hold is a no-op.","operationId":"revokeValidatorClass","parameters":[{"description":"Project ID.","in":"path","name":"projectId","required":true,"schema":{"description":"Project ID.","format":"uuid","type":"string"}},{"description":"Validator (user) ID.","in":"path","name":"validatorId","required":true,"schema":{"description":"Validator (user) ID.","format":"uuid","type":"string"}},{"description":"Class ID to revoke.","in":"path","name":"classId","required":true,"schema":{"description":"Class ID to revoke.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeValidatorClassOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Revoke a validator's class on a project","tags":["Validators"]}},"/validators":{"get":{"description":"With ?email= set, looks up the single org validator matching that email (case-insensitive). Without ?email=, lists the org validator roster (org is_validator members union project-assigned validators), cursor-paginated, newest joined_at first. A lookup that matches no user, or a user who is neither an org validator nor assigned to a project in the org, returns 404 validator_not_found — the API never leaks whether a user exists outside your organization.","operationId":"listValidators","parameters":[{"description":"Optional email to look up a single org validator (case-insensitive). When set, limit/cursor are ignored.","explode":false,"in":"query","name":"email","schema":{"description":"Optional email to look up a single org validator (case-insensitive). When set, limit/cursor are ignored.","type":"string"}},{"description":"Maximum number of validators to return (1-100); list mode only.","explode":false,"in":"query","name":"limit","schema":{"default":20,"description":"Maximum number of validators to return (1-100); list mode only.","format":"int64","maximum":100,"minimum":1,"type":"integer"}},{"description":"Opaque pagination cursor from a previous response's nextCursor; list mode only.","explode":false,"in":"query","name":"cursor","schema":{"description":"Opaque pagination cursor from a previous response's nextCursor; list mode only.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListValidatorsOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"List or look up validators","tags":["Validators"]}},"/validators/{validatorId}/assign":{"post":{"description":"Assign a validator to one or more projects in the API key's organization. The validator must already be a validator of the organization unless autoEnroll is set (which promotes an existing org member to validator first). Every project must belong to the organization. Applied as an all-or-nothing batch.","operationId":"assignValidator","parameters":[{"description":"Validator (user) ID.","in":"path","name":"validatorId","required":true,"schema":{"description":"Validator (user) ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignValidatorInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignValidatorOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Assign a validator to projects","tags":["Validators"]}},"/validators/{validatorId}/projects":{"get":{"description":"List the projects a validator is currently assigned to within the API key's organization. Returns an empty list for an unknown validator or one with no assignments.","operationId":"listValidatorProjects","parameters":[{"description":"Validator (user) ID.","in":"path","name":"validatorId","required":true,"schema":{"description":"Validator (user) ID.","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListValidatorProjectsOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"List a validator's project assignments","tags":["Validators"]}},"/validators/{validatorId}/unassign":{"post":{"description":"Revoke a validator's assignment to one or more projects in the API key's organization. Idempotent: unassigning a project the validator is not assigned to is a no-op. Class grants are separate — revoke them via DELETE /projects/{projectId}/validators/{validatorId}/classes/{classId}.","operationId":"unassignValidator","parameters":[{"description":"Validator (user) ID.","in":"path","name":"validatorId","required":true,"schema":{"description":"Validator (user) ID.","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnassignValidatorInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnassignValidatorOutputBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperStatusError"}}},"description":"Error"}},"summary":"Unassign a validator from projects","tags":["Validators"]}}},"security":[{"bearerAuth":[]}],"servers":[{"url":"/developer/v1"}],"tags":[{"name":"System"},{"name":"Projects"},{"name":"Datapoints"},{"name":"Upload Sessions"},{"name":"Validators"}]}