{"openapi":"3.1.0","jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","info":{"title":"Moddingflow Public API","version":"1.0.0-foundation","summary":"Public v1 contract foundation for Moddingflow clients.","description":"The public API contract is separate from the internal Rust gateway. Public clients use https://api.moddingflow.com/v1, with https://www.moddingflow.com/api/v1 kept as a compatibility BFF namespace. /v1/internal remains reserved for unstable internal actions and is intentionally absent from this public spec."},"servers":[{"url":"https://api.moddingflow.com/v1","description":"Primary public API namespace."},{"url":"https://www.moddingflow.com/api/v1","description":"Compatibility namespace behind the current website domain."}],"tags":[{"name":"Catalog","description":"Read-only mod catalog resources for Mod Manager, website integrations, and automation."},{"name":"Uploads","description":"Upload session contract foundation. The backing executor remains Rust-owned."},{"name":"Publishing","description":"Creator mod and version draft, upload, publish, rollback, audit, and moderation-hook writes."},{"name":"Downloads","description":"Artifact download resolution for public and authenticated clients."},{"name":"Install Plans","description":"Dependency-aware install planning for Mod Manager clients."},{"name":"Auth","description":"OAuth clients, token lifecycle, PAT/API key metadata, scopes, and trusted introspection."},{"name":"Agent Gateway","description":"Delegated AI agent token exchange, tool registry, policy sandbox, audit, and dry-run-safe tool calls."},{"name":"Webhooks","description":"Webhook subscription management, signed delivery payloads, retry state, and manual redelivery for automation clients."}],"externalDocs":{"description":"Localized public developer guide, API reference, SDK source, changelog, FAQ, and migration guidance.","url":"https://moddingflow.com/api/?lang=en"},"x-moddingflow-documentation":{"openApiSource":"Rust-owned generator emits openapi/moddingflow-public-api.v1.json; website exposes the committed artifact at /api/openapi.json.","publicPages":{"docs":"/api/","reference":"/api/reference/","sdkSource":"/api/sdk/","faq":"/support/faq/","changelogMigration":"/api/changelog/"},"exampleCoverage":{"successfulRequests":["abortUploadSession","authorizeOAuthClient","completeUploadSession","createApiKey","createDeviceAuthorization","createModDraft","createModVersion","createUploadSession","createWebhookSubscription","exchangeOAuthToken","getApiKeyCapabilities","getGame","getJsonWebKeySet","getMod","getModVersion","getOAuthAuthorizationServerMetadata","getOidcDiscovery","getUploadSession","introspectOAuthToken","listAgentGatewayTools","listApiKeys","listCatalogCategories","listGames","listModDependencies","listModTeamMembers","listModVersions","listMods","listWebhookDeliveries","listWebhookSubscriptions","publishModVersion","redeliverWebhookDelivery","removeModTeamMember","resolveDownload","resolveDownloadFallback","resolveInstallPlan","revokeApiKey","revokeOAuthToken","rollbackModVersion","rotateApiKey","rotateWebhookSubscriptionSecret","runAgentGatewayTool","searchCatalog","setModTeamMember","signUploadParts","updateModDraft","updateModVersion","validateUploadRequest"],"problemDetails":"Shared application/problem+json responses include RFC 9457 examples for 400, 401, 403, 404, 409, 410, 412, 422, 429 and 500.","uploadFlow":"Examples document create, poll, sign parts, complete and abort upload-session steps plus async job polling.","downloadFlow":"Examples document download-grant resolve and fallback refresh without exposing storage-provider identities as durable contract keys.","webhookVerification":"Examples document X-Moddingflow-Signature, X-Moddingflow-Timestamp, X-Moddingflow-Delivery-Id and HMAC-SHA256 over `${timestamp}.${raw_body}`.","allPublicOperations":{"count":47,"operationIds":["abortUploadSession","authorizeOAuthClient","completeUploadSession","createApiKey","createDeviceAuthorization","createModDraft","createModVersion","createUploadSession","createWebhookSubscription","exchangeOAuthToken","getApiKeyCapabilities","getGame","getJsonWebKeySet","getMod","getModVersion","getOAuthAuthorizationServerMetadata","getOidcDiscovery","getUploadSession","introspectOAuthToken","listAgentGatewayTools","listApiKeys","listCatalogCategories","listGames","listModDependencies","listModTeamMembers","listModVersions","listMods","listWebhookDeliveries","listWebhookSubscriptions","publishModVersion","redeliverWebhookDelivery","removeModTeamMember","resolveDownload","resolveDownloadFallback","resolveInstallPlan","revokeApiKey","revokeOAuthToken","rollbackModVersion","rotateApiKey","rotateWebhookSubscriptionSecret","runAgentGatewayTool","searchCatalog","setModTeamMember","signUploadParts","updateModDraft","updateModVersion","validateUploadRequest"],"standardOpenApiLocations":["Parameter Object example","Media Type Object examples","Response Header Object example for bodyless redirects"]}},"releaseDocumentation":{"migrationGuideRequired":true,"changelogEntryRequired":true,"finalReadinessChecklistRequired":true,"knownLimitationsReleaseNotesRequired":true,"localizedDocsRequired":["ru","en","de"],"validationGates":["npm run api:openapi:check","npm run api:release:gates","npm run api:sdks:check","npm run locales:validate"]}},"x-moddingflow-client-identity":{"schemaVersion":1,"userAgent":{"required":true,"format":"ApplicationName/ApplicationVersion (+contact-url-or-email)","contactGuidance":"Include a maintained HTTPS support URL or contact email in the User-Agent comment."},"applicationHeaders":{"name":"X-Moddingflow-Client","version":"X-Moddingflow-Client-Version","patMutations":"Both headers are required and must use stable, non-secret application identity values.","oauth":"The authenticated OAuth client_id is authoritative; supplied application-name headers must match it."},"failure":{"code":"invalid_client_identity","status":400,"ordering":"Rejected before rate-limit bucket consumption and business side effects."}},"x-moddingflow-error-registry":{"schemaVersion":1,"owner":"public_api_contract","sourceOfTruth":"Rust-owned OpenAPI generator in RustBackend/crates/contracts/src/lib.rs","evolutionPolicy":{"additiveOnlyWithinV1":true,"removeOrRenameRequiresNewMajor":true,"semanticOrStatusChangeRequiresNewCode":true,"unknownCodeBehavior":"Clients preserve the unknown code, numeric HTTP status, headers, payload, request_id, and trace_id, then fall back to generic status handling."},"extensionPopulationRules":{"retryable":"Always present. True only when retrying the same logical request is safe after backoff or the documented server delay.","retry_after_seconds":"Present only for retryable codes when the server knows a bounded delay; always present on 429 and mirrored by Retry-After.","quota_bucket":"Present only for rate or quota decisions and contains a stable non-sensitive bucket class, never an IP, token, user id, or raw storage key.","required_scopes":"Present only when granting the listed minimum scopes can make this exact authorization decision pass.","fields":"Present only for safe field-level validation details; clients branch on field code, never localized prose.","docs_slug":"Always present as errors.{code}; it is the stable lookup key in docs/public-api/ERRORS.md, not a URL."},"codes":{"access_denied":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.access_denied","allowedExtensions":[],"semantic":"The resource owner denied the OAuth authorization."},"account_blocked":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.account_blocked","allowedExtensions":[],"semantic":"The account is not eligible to perform this operation."},"archive_dangerous_entry":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_dangerous_entry","allowedExtensions":[],"semantic":"The archive contains a dangerous filesystem entry."},"archive_duplicate_path":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_duplicate_path","allowedExtensions":[],"semantic":"The archive contains duplicate or aliased paths."},"archive_empty":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_empty","allowedExtensions":[],"semantic":"The uploaded archive has no usable content."},"archive_encrypted_entry":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_encrypted_entry","allowedExtensions":[],"semantic":"The archive contains an encrypted entry that cannot be inspected."},"archive_magic_mismatch":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_magic_mismatch","allowedExtensions":[],"semantic":"The archive bytes do not match the declared archive format."},"archive_mime_mismatch":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_mime_mismatch","allowedExtensions":[],"semantic":"The archive media type conflicts with its validated format."},"archive_path_traversal":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_path_traversal","allowedExtensions":[],"semantic":"The archive contains a path traversal entry."},"archive_symlink_entry":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_symlink_entry","allowedExtensions":[],"semantic":"The archive contains a symbolic-link entry."},"archive_too_large":{"httpStatuses":[413],"retryable":false,"docsSlug":"errors.archive_too_large","allowedExtensions":[],"semantic":"The archive exceeds the accepted upload size."},"archive_validation_failed":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.archive_validation_failed","allowedExtensions":["fields"],"semantic":"The archive failed structural safety validation."},"authorization_pending":{"httpStatuses":[400],"retryable":true,"docsSlug":"errors.authorization_pending","allowedExtensions":["retry_after_seconds"],"semantic":"OAuth device authorization has not reached a terminal decision."},"av_scan_rejected":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.av_scan_rejected","allowedExtensions":[],"semantic":"The archive security scan rejected or quarantined the upload."},"checksum_not_verified":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.checksum_not_verified","allowedExtensions":[],"semantic":"The file checksum has not reached a verified state."},"confirmation_required":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.confirmation_required","allowedExtensions":["required_scopes"],"semantic":"A confirmed-write scope or valid confirmation proof is required."},"cursor_context_mismatch":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.cursor_context_mismatch","allowedExtensions":[],"semantic":"The cursor was created for different normalized filters, sort, or snapshot context."},"delegated_scope_denied":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.delegated_scope_denied","allowedExtensions":["required_scopes"],"semantic":"A delegated credential attempted to grant a scope it does not hold."},"download_rate_limited":{"httpStatuses":[429],"retryable":true,"docsSlug":"errors.download_rate_limited","allowedExtensions":["download_job","quota_bucket","retry_after_seconds"],"semantic":"A durable download job is waiting for its bounded retry window before provider work."},"download_session_expired":{"httpStatuses":[410],"retryable":false,"docsSlug":"errors.download_session_expired","allowedExtensions":[],"semantic":"The download capability expired and must be resolved again."},"download_session_not_found":{"httpStatuses":[404],"retryable":false,"docsSlug":"errors.download_session_not_found","allowedExtensions":[],"semantic":"The download capability does not exist or is not visible to the caller."},"duplicate_version":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.duplicate_version","allowedExtensions":[],"semantic":"The mod already has the requested version identity."},"entitlement_required":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.entitlement_required","allowedExtensions":[],"semantic":"The requested download path requires an entitlement the caller does not hold."},"expired_token":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.expired_token","allowedExtensions":[],"semantic":"The OAuth device token expired."},"fallback_not_allowed":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.fallback_not_allowed","allowedExtensions":[],"semantic":"The download fallback policy does not allow this request."},"feature_disabled":{"httpStatuses":[503],"retryable":false,"docsSlug":"errors.feature_disabled","allowedExtensions":[],"semantic":"The feature is intentionally disabled by an operational gate."},"file_not_found":{"httpStatuses":[404],"retryable":false,"docsSlug":"errors.file_not_found","allowedExtensions":[],"semantic":"The file does not exist or is not visible to the caller."},"file_not_ready":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.file_not_ready","allowedExtensions":[],"semantic":"The file exists but has not reached a downloadable state."},"forbidden":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.forbidden","allowedExtensions":["required_scopes"],"semantic":"The authenticated caller is not authorized for the requested action."},"gone":{"httpStatuses":[410],"retryable":false,"docsSlug":"errors.gone","allowedExtensions":[],"semantic":"The requested capability or resource intentionally expired."},"idempotency_conflict":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.idempotency_conflict","allowedExtensions":[],"semantic":"The Idempotency-Key was already used with a different normalized request."},"idempotency_in_progress":{"httpStatuses":[409],"retryable":true,"docsSlug":"errors.idempotency_in_progress","allowedExtensions":["retry_after_seconds"],"semantic":"The same idempotent request is still being executed."},"idempotency_key_required":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.idempotency_key_required","allowedExtensions":[],"semantic":"The mutation requires an Idempotency-Key header."},"idempotency_replay_unavailable":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.idempotency_replay_unavailable","allowedExtensions":[],"semantic":"The mutation may have committed but its one-time response cannot be recovered safely."},"insufficient_scope":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.insufficient_scope","allowedExtensions":["required_scopes"],"semantic":"The credential lacks a required public API scope."},"internal_error":{"httpStatuses":[500],"retryable":true,"docsSlug":"errors.internal_error","allowedExtensions":["retry_after_seconds"],"semantic":"An unexpected server failure prevented completion."},"invalid_client":{"httpStatuses":[401],"retryable":false,"docsSlug":"errors.invalid_client","allowedExtensions":[],"semantic":"OAuth client identity or authentication is invalid."},"invalid_client_identity":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_client_identity","allowedExtensions":[],"semantic":"The request lacks a meaningful User-Agent or has missing, malformed, or mismatched stable application identity headers."},"invalid_file_id":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_file_id","allowedExtensions":[],"semantic":"The file identifier is syntactically invalid."},"invalid_grant":{"httpStatuses":[400,409],"retryable":false,"docsSlug":"errors.invalid_grant","allowedExtensions":[],"semantic":"The OAuth grant is invalid, consumed, revoked, or no longer eligible."},"invalid_input":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_input","allowedExtensions":["fields"],"semantic":"One or more request values are invalid."},"invalid_json":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_json","allowedExtensions":["fields"],"semantic":"The request body is missing or is not valid JSON."},"invalid_metadata":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_metadata","allowedExtensions":["fields"],"semantic":"The supplied mod, version, or upload metadata is invalid."},"invalid_part_numbers":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_part_numbers","allowedExtensions":[],"semantic":"Multipart upload part numbers are invalid."},"invalid_parts":{"httpStatuses":[400,409],"retryable":false,"docsSlug":"errors.invalid_parts","allowedExtensions":[],"semantic":"Multipart upload parts are missing, duplicated, or conflict with provider state."},"invalid_request":{"httpStatuses":[400,413],"retryable":false,"docsSlug":"errors.invalid_request","allowedExtensions":["fields"],"semantic":"The request syntax, encoding, or shape is invalid."},"invalid_scope":{"httpStatuses":[400,403],"retryable":false,"docsSlug":"errors.invalid_scope","allowedExtensions":["required_scopes"],"semantic":"An OAuth or application scope request is invalid or not grantable."},"invalid_sha256":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_sha256","allowedExtensions":[],"semantic":"The SHA-256 value has an invalid representation."},"invalid_size":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_size","allowedExtensions":[],"semantic":"The declared file size is invalid."},"invalid_staging_object":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.invalid_staging_object","allowedExtensions":[],"semantic":"The staged upload object does not satisfy the upload contract."},"invalid_state":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.invalid_state","allowedExtensions":[],"semantic":"The resource is not in a state that permits the requested action."},"invalid_state_transition":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.invalid_state_transition","allowedExtensions":[],"semantic":"The requested lifecycle transition is not allowed."},"invalid_tool_output":{"httpStatuses":[500],"retryable":true,"docsSlug":"errors.invalid_tool_output","allowedExtensions":["retry_after_seconds"],"semantic":"An Agent Gateway tool produced output that violated its public contract."},"missing_sha256":{"httpStatuses":[400,409],"retryable":false,"docsSlug":"errors.missing_sha256","allowedExtensions":[],"semantic":"A required SHA-256 value is absent."},"not_found":{"httpStatuses":[404],"retryable":false,"docsSlug":"errors.not_found","allowedExtensions":[],"semantic":"The resource does not exist or is intentionally hidden from the caller."},"origin_mismatch":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.origin_mismatch","allowedExtensions":[],"semantic":"A browser control-plane request failed the trusted-origin check."},"part_checksum_mismatch":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.part_checksum_mismatch","allowedExtensions":[],"semantic":"A multipart part checksum does not match provider-confirmed state."},"part_size_mismatch":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.part_size_mismatch","allowedExtensions":[],"semantic":"A multipart part size does not match the upload plan."},"precondition_failed":{"httpStatuses":[412],"retryable":false,"docsSlug":"errors.precondition_failed","allowedExtensions":[],"semantic":"A conditional request precondition such as If-Match failed."},"precondition_required":{"httpStatuses":[428],"retryable":false,"docsSlug":"errors.precondition_required","allowedExtensions":[],"semantic":"A mutable resource write omitted its required If-Match validator."},"rate_limited":{"httpStatuses":[429],"retryable":true,"docsSlug":"errors.rate_limited","allowedExtensions":["quota_bucket","retry_after_seconds"],"semantic":"A request or byte quota was exhausted before side effects."},"request_body_too_large":{"httpStatuses":[413],"retryable":false,"docsSlug":"errors.request_body_too_large","allowedExtensions":[],"semantic":"The request body exceeds the route-specific byte limit."},"resource_scope_denied":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.resource_scope_denied","allowedExtensions":[],"semantic":"The credential is not granted to the target resource."},"server_error":{"httpStatuses":[500,503],"retryable":true,"docsSlug":"errors.server_error","allowedExtensions":["retry_after_seconds"],"semantic":"An OAuth server failure prevented completion."},"service_unavailable":{"httpStatuses":[503],"retryable":true,"docsSlug":"errors.service_unavailable","allowedExtensions":["retry_after_seconds"],"semantic":"A required service or safe configuration is temporarily unavailable."},"session_expired":{"httpStatuses":[410],"retryable":false,"docsSlug":"errors.session_expired","allowedExtensions":[],"semantic":"The upload session expired and a new session is required."},"session_not_abortable":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.session_not_abortable","allowedExtensions":[],"semantic":"The upload session has reached a state that cannot be aborted."},"session_not_found":{"httpStatuses":[404],"retryable":false,"docsSlug":"errors.session_not_found","allowedExtensions":[],"semantic":"The upload session does not exist or is not visible to the caller."},"session_not_multipart":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.session_not_multipart","allowedExtensions":[],"semantic":"Multipart operations were requested for a single-part upload session."},"session_not_uploaded":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.session_not_uploaded","allowedExtensions":[],"semantic":"The upload session has not reached the uploaded state."},"session_not_uploading":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.session_not_uploading","allowedExtensions":[],"semantic":"The upload session is not accepting upload parts."},"sha256_mismatch":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.sha256_mismatch","allowedExtensions":[],"semantic":"The uploaded bytes do not match the expected SHA-256."},"size_mismatch":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.size_mismatch","allowedExtensions":[],"semantic":"The uploaded byte size does not match the expected size."},"slow_down":{"httpStatuses":[400,429],"retryable":true,"docsSlug":"errors.slow_down","allowedExtensions":["quota_bucket","retry_after_seconds"],"semantic":"The OAuth client must wait before polling or retrying."},"step_up_required":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.step_up_required","allowedExtensions":[],"semantic":"The account must complete the configured MFA step-up before the action."},"team_role_denied":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.team_role_denied","allowedExtensions":[],"semantic":"The caller's team role does not permit the action."},"tool_not_found":{"httpStatuses":[404],"retryable":false,"docsSlug":"errors.tool_not_found","allowedExtensions":[],"semantic":"The requested Agent Gateway tool is not registered."},"unauthorized":{"httpStatuses":[401],"retryable":false,"docsSlug":"errors.unauthorized","allowedExtensions":[],"semantic":"The bearer credential is missing, invalid, expired, or revoked."},"unauthorized_client":{"httpStatuses":[403],"retryable":false,"docsSlug":"errors.unauthorized_client","allowedExtensions":[],"semantic":"The OAuth client is not allowed to use the requested grant or flow."},"unsupported_archive_extension":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.unsupported_archive_extension","allowedExtensions":[],"semantic":"The filename uses an archive extension that is not accepted."},"unsupported_grant_type":{"httpStatuses":[400],"retryable":false,"docsSlug":"errors.unsupported_grant_type","allowedExtensions":[],"semantic":"The OAuth token endpoint does not support the requested grant type."},"unsupported_version":{"httpStatuses":[422],"retryable":false,"docsSlug":"errors.unsupported_version","allowedExtensions":[],"semantic":"The requested game or API-facing version value is not supported."},"user_not_found":{"httpStatuses":[404],"retryable":false,"docsSlug":"errors.user_not_found","allowedExtensions":[],"semantic":"The requested user or team member could not be resolved."},"validation_failed":{"httpStatuses":[422],"retryable":false,"docsSlug":"errors.validation_failed","allowedExtensions":["fields"],"semantic":"Field-level validation failed for a syntactically valid request."},"version_not_ready":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.version_not_ready","allowedExtensions":[],"semantic":"The mod version has not reached a state that permits the action."},"zip64_not_supported":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.zip64_not_supported","allowedExtensions":[],"semantic":"The ZIP uses ZIP64 structures that are not accepted by the safety validator."},"zip_bomb_guard":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.zip_bomb_guard","allowedExtensions":[],"semantic":"The ZIP exceeds compression-ratio or aggregate expansion safety limits."},"zip_central_directory_too_large":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.zip_central_directory_too_large","allowedExtensions":[],"semantic":"The ZIP central directory exceeds the inspection limit."},"zip_entry_limit":{"httpStatuses":[409],"retryable":false,"docsSlug":"errors.zip_entry_limit","allowedExtensions":[],"semantic":"The ZIP contains more entries than the safety limit."}}},"x-moddingflow-upload-contract":{"validateOnlyEndpoint":"POST /v1/uploads:validate","canonicalEndpoints":{"create":"POST /v1/uploads","status":"GET /v1/uploads/{upload_id}","parts":"POST /v1/uploads/{upload_id}/parts","complete":"POST /v1/uploads/{upload_id}/complete","cancel":"POST /v1/uploads/{upload_id}/abort","publish":"POST /v1/mods/{mod_id}/versions/{version_id}/publish"},"canonicalLifecycle":["draft","uploading","uploaded","validating","scanning","quarantined","ready","published","failed","cancelled"],"legacyStatusCompatibility":"The legacy status field UploadSessionStatusResponse.data.status retains created/processing/aborted/expired aliases for v1 clients. data.lifecycleStatus is the canonical end-to-end automation state.","idempotency":{"requiredFor":["createUploadSession","completeUploadSession"],"sameKeySameBodyReplays":true,"sameKeyDifferentBodyCode":"idempotency_conflict","staleClaimsRecoverable":true},"metadata":{"integrity":["expectedHashes.sha256","sizeBytes"],"upload":["archiveManifest","buildMetadata"],"version":["version","changelog","file_category","game_slug","game_versions","loader_platforms","dependencies","requirements","build_metadata"]},"errorCodes":["account_blocked","archive_dangerous_entry","archive_duplicate_path","archive_empty","archive_encrypted_entry","archive_magic_mismatch","archive_mime_mismatch","archive_path_traversal","archive_symlink_entry","archive_too_large","archive_validation_failed","av_scan_rejected","checksum_not_verified","duplicate_version","file_not_found","forbidden","idempotency_conflict","idempotency_in_progress","idempotency_key_required","idempotency_replay_unavailable","insufficient_scope","invalid_file_id","invalid_input","invalid_json","invalid_metadata","invalid_part_numbers","invalid_parts","invalid_request","invalid_sha256","invalid_size","invalid_staging_object","invalid_state_transition","missing_sha256","not_found","part_checksum_mismatch","part_size_mismatch","rate_limited","request_body_too_large","service_unavailable","session_expired","session_not_abortable","session_not_found","session_not_multipart","session_not_uploaded","session_not_uploading","sha256_mismatch","size_mismatch","unauthorized","unsupported_archive_extension","version_not_ready","zip64_not_supported","zip_bomb_guard","zip_central_directory_too_large","zip_entry_limit"]},"x-moddingflow-boundaries":{"publicNamespaces":["https://api.moddingflow.com/v1","https://www.moddingflow.com/api/v1"],"internalReservedNamespace":"/v1/internal","internalNamespacePolicy":"Internal gateway actions stay under /v1/internal and are not part of the stable public API.","accessModeMatrix":{"anonymousReadOnly":{"credentialRequired":false,"operationIds":["listGames","getGame","listMods","getMod","listModVersions","getModVersion","listModDependencies","listCatalogCategories","searchCatalog","resolveDownload","resolveDownloadFallback","resolveInstallPlan"],"contract":"These operations expose only public catalog, install-plan, and downloadable-artifact views when no credential is supplied. Anonymous access never grants private, restricted, paid, draft, team, or account data."},"optionalAuthentication":{"credentialRequired":false,"appliesToOperationIds":["resolveDownload","resolveDownloadFallback","resolveInstallPlan"],"acceptedSchemes":["OAuth2Pkce","BearerAccessToken"],"contract":"A valid credential adds actor-bound entitlement context to public artifact and install-plan resolution, but omitting it remains a supported public read-only mode. For these optional-auth operations an invalid supplied credential fails closed instead of silently falling back to anonymous. Catalog list/get/search operations use the anonymous public view and do not require a credential."},"requiredAuthentication":{"credentialRequired":true,"operationIds":["createModDraft","updateModDraft","createModVersion","updateModVersion","createUploadSession","validateUploadRequest","getUploadSession","signUploadParts","completeUploadSession","abortUploadSession","publishModVersion","rollbackModVersion","listApiKeys","createApiKey","getApiKeyCapabilities","rotateApiKey","revokeApiKey","listModTeamMembers","setModTeamMember","removeModTeamMember","revokeOAuthToken","listAgentGatewayTools","runAgentGatewayTool","listWebhookSubscriptions","createWebhookSubscription","rotateWebhookSubscriptionSecret","listWebhookDeliveries","redeliverWebhookDelivery"],"contract":"Account, write, upload, publish, team, webhook, and agent operations require a valid credential with every documented scope and resource grant."},"oauthProtocol":{"operationIds":["authorizeOAuthClient","createDeviceAuthorization","exchangeOAuthToken","getOidcDiscovery","introspectOAuthToken"],"contract":"OAuth protocol endpoints apply their own client, browser-session, consent, PKCE, device-code, and trusted-client rules; they are not anonymous public-data operations."}},"externalResources":["/v1/games","/v1/games/{game_slug}","/v1/mods","/v1/mods/{mod_id}","/v1/mods/{mod_id}/versions","/v1/mods/{mod_id}/versions/{version_id}","/v1/mods/{mod_id}/dependencies","/v1/categories","/v1/search","/v1/uploads","/v1/uploads:validate","/v1/uploads/{upload_id}","/v1/downloads/{artifact_id}/resolve","/v1/downloads/{artifact_id}/fallback","/v1/install-plans:resolve","/v1/api-keys","/v1/api-keys/capabilities","/v1/api-keys/{api_key_id}","/v1/api-keys/{api_key_id}/rotate","/v1/mods/{mod_id}/team-members","/v1/mods/{mod_id}/team-members/{user_id}","/v1/uploads/{upload_id}/parts","/v1/uploads/{upload_id}/complete","/v1/uploads/{upload_id}/abort","/v1/mods/{mod_id}/versions/{version_id}/publish","/v1/mods/{mod_id}/versions/{version_id}/rollback","/v1/agent/tools","/v1/agent/tools:run","/v1/webhook-subscriptions","/v1/webhook-subscriptions/{subscription_id}/rotate-secret","/v1/webhook-deliveries","/v1/webhook-deliveries/{delivery_id}/redeliver"],"internalActions":["/v1/internal/..."],"contractVerification":{"coverageOwner":"Stable public API v1 release gate","stableDtoSchemaTests":{"checklistItem":"stable_public_dto_schema","commands":["npx vitest run tests/unit/lib/publicApi/live-handler-response-shape-fixtures.test.ts tests/unit/scripts/public-api-sdks.test.ts"],"testFiles":["tests/unit/lib/publicApi/live-handler-response-shape-fixtures.test.ts","tests/unit/scripts/public-api-sdks.test.ts"],"asserts":["live route success payloads include every OpenAPI-required response field","generated TypeScript, Python and C# DTOs keep schema-derived required fields"]},"openApiGenerationValidation":{"checklistItem":"openapi_generation_validation","commands":["npm run api:openapi:check","npx vitest run tests/unit/scripts/public-api-openapi-lint.test.ts tests/unit/scripts/public-api-release-gates.test.ts"],"testFiles":["tests/unit/scripts/public-api-openapi-lint.test.ts","tests/unit/scripts/public-api-release-gates.test.ts"],"asserts":["Rust generator, OpenAPI lint, compatibility, Rust contract tests, SDK check and release gates run as one contract command","route/OpenAPI parity rejects public runtime routes that are missing from the spec"]},"problemDetailsTests":{"checklistItem":"problem_details_errors","commands":["npx vitest run tests/unit/lib/publicApi/writeAuth.server.test.ts tests/unit/lib/publicApi/downloadRoutes.server.test.ts tests/unit/lib/publicApi/uploadRoutes.server.test.ts tests/unit/scripts/public-api-sdks.test.ts"],"testFiles":["tests/unit/lib/publicApi/writeAuth.server.test.ts","tests/unit/lib/publicApi/downloadRoutes.server.test.ts","tests/unit/lib/publicApi/uploadRoutes.server.test.ts","tests/unit/scripts/public-api-sdks.test.ts"],"asserts":["typical auth, permission, validation, rate-limit and lifecycle errors expose RFC 9457 fields","SDK errors branch by type/code without parsing localized detail text"]},"cursorPaginationTests":{"checklistItem":"cursor_pagination","commands":["npx vitest run tests/unit/app/api/public-api-catalog.route.test.ts tests/unit/supabase/public-api-read-only-catalog-sql-source.test.ts tests/unit/scripts/public-api-openapi-lint.test.ts"],"testFiles":["tests/unit/app/api/public-api-catalog.route.test.ts","tests/unit/supabase/public-api-read-only-catalog-sql-source.test.ts","tests/unit/scripts/public-api-openapi-lint.test.ts"],"asserts":["mutable catalog lists use opaque v1 keyset cursors instead of offset/page-number cursors","runtime validates cursor, limit, filters and sort before RPC work"]},"uploadSessionLifecycleTests":{"checklistItem":"upload_session_lifecycle","commands":["npx vitest run tests/unit/lib/publicApi/uploadRoutes.server.test.ts tests/unit/lib/publicApi/writeIdempotency.server.test.ts tests/unit/lib/publicApi/modWriteRoutes.server.test.ts tests/unit/app/api/r2/uploads.route.test.ts tests/unit/lib/publicApi/live-handler-response-shape-fixtures.test.ts tests/unit/supabase/public-api-upload-contract-sql-source.test.ts"],"testFiles":["tests/unit/lib/publicApi/uploadRoutes.server.test.ts","tests/unit/lib/publicApi/writeIdempotency.server.test.ts","tests/unit/lib/publicApi/modWriteRoutes.server.test.ts","tests/unit/app/api/r2/uploads.route.test.ts","tests/unit/lib/publicApi/live-handler-response-shape-fixtures.test.ts","tests/unit/supabase/public-api-upload-contract-sql-source.test.ts"],"asserts":["create, validate-only, status, sign-parts, complete and cancel endpoints keep the canonical upload lifecycle contract","completion exposes async job/status metadata, retry recovery, duplicate-version denial and integrity failure reasons"]},"downloadGrantLifecycleTests":{"checklistItem":"download_grant_lifecycle","commands":["npx vitest run tests/unit/lib/publicApi/downloadJobs.server.test.ts tests/unit/lib/publicApi/downloadRoutes.server.test.ts tests/unit/lib/publicApi/writeResponse.test.ts tests/unit/lib/r2/modArchives.test.ts tests/unit/app/api/r2/downloads.route.test.ts tests/unit/scripts/public-api-stage04-durability-probe.test.ts tests/unit/supabase/public-api-download-install-plan-sql-source.test.ts tests/unit/supabase/public-api-stage04-legal-hold-gc-download-jobs-sql-source.test.ts"],"testFiles":["tests/unit/lib/publicApi/downloadJobs.server.test.ts","tests/unit/lib/publicApi/downloadRoutes.server.test.ts","tests/unit/lib/publicApi/writeResponse.test.ts","tests/unit/lib/r2/modArchives.test.ts","tests/unit/app/api/r2/downloads.route.test.ts","tests/unit/scripts/public-api-stage04-durability-probe.test.ts","tests/unit/supabase/public-api-download-install-plan-sql-source.test.ts","tests/unit/supabase/public-api-stage04-legal-hold-gc-download-jobs-sql-source.test.ts"],"asserts":["resolve creates or resumes an actor-bound durable job with one short-lived grant and fallback requires downloadSessionId","repeated 429 preserves job, grant and monotonic progress without provider effects; expired, revoked or unusable grants return stable machine-readable errors","reference-aware legal hold and provider-first blob GC preserve referenced and held bytes and retain a verified restore path"]},"oauthPatScopeTests":{"checklistItem":"oauth_pat_scopes","commands":["npx vitest run tests/unit/lib/publicApi/oauthApplications.server.test.ts tests/unit/lib/publicApi/oauthLifecycle.server.test.ts tests/unit/lib/publicApi/writeAuth.server.test.ts tests/unit/lib/publicApi/apiKeyRoutes.server.test.ts tests/unit/lib/publicApi/resourceAuthorization.server.test.ts tests/unit/app/oauth-consent.test.tsx tests/unit/app/api/public-api-oidc.route.test.ts tests/unit/components/settings/DeveloperSettingsPanel.test.tsx tests/unit/supabase/public-api-auth-lifecycle-sql-source.test.ts tests/unit/supabase/public-api-complete-auth-model-sql-source.test.ts tests/unit/supabase/public-api-team-api-keys-sql-source.test.ts"],"testFiles":["tests/unit/lib/publicApi/oauthApplications.server.test.ts","tests/unit/lib/publicApi/oauthLifecycle.server.test.ts","tests/unit/lib/publicApi/writeAuth.server.test.ts","tests/unit/lib/publicApi/apiKeyRoutes.server.test.ts","tests/unit/lib/publicApi/resourceAuthorization.server.test.ts","tests/unit/app/oauth-consent.test.tsx","tests/unit/app/api/public-api-oidc.route.test.ts","tests/unit/components/settings/DeveloperSettingsPanel.test.tsx","tests/unit/supabase/public-api-auth-lifecycle-sql-source.test.ts","tests/unit/supabase/public-api-complete-auth-model-sql-source.test.ts","tests/unit/supabase/public-api-team-api-keys-sql-source.test.ts"],"evidenceFiles":["src/lib/publicApi/oauthApplications.server.ts","src/lib/publicApi/oauthConsent.server.ts","src/lib/publicApi/oauthLifecycle.server.ts","src/app/api/account/developer/applications/route.ts","src/app/api/account/developer/applications/[applicationId]/route.ts","src/app/api/account/developer/applications/[applicationId]/scopes/route.ts","src/components/settings/DeveloperSettingsPanel.tsx","src/app/oauth/consent/page.tsx","src/app/oauth/consent/actions.ts","src/app/oauth/device/page.tsx","src/app/oauth/device/actions.ts","../supabase/migrations/20260711120000_public_api_complete_auth_model.sql","docs/public-api/ADR-0003-COMPLETE-PUBLIC-API-AUTH-MODEL.ru.md"],"asserts":["Settings > Developer and its internal account API provide self-service public-app registration, exact redirect URI management, contact ownership, disable, and base-scope access without support intervention","authorization code with PKCE and device authorization both require explicit user consent; device polling enforces interval, slow_down backoff, denial, expiry, and single consumption","review scopes and app disable require an AAL2 step-up, while agent:* and admin:* remain internal-only and unknown scopes fail closed","access-token revoke is effective on the next protected call, refresh-token rotation detects reuse and revokes the token family, and PAT rotation permits only the documented ten-minute overlap","service-role/internal secrets are rejected as viewer credentials"]},"webhookSignatureVerificationTests":{"checklistItem":"webhook_signature_verification","commands":["npx vitest run tests/unit/lib/publicApi/webhookRoutes.server.test.ts tests/unit/supabase/public-api-webhook-contract-sql-source.test.ts tests/unit/scripts/public-api-openapi-lint.test.ts"],"testFiles":["tests/unit/lib/publicApi/webhookRoutes.server.test.ts","tests/unit/supabase/public-api-webhook-contract-sql-source.test.ts","tests/unit/scripts/public-api-openapi-lint.test.ts"],"asserts":["webhook payloads document HMAC-SHA256 signature headers and delivery id","subscription secrets remain write-only and verification examples stay in OpenAPI"]},"webhookRetryRedeliveryTests":{"checklistItem":"webhook_retry_redelivery","commands":["npx vitest run tests/unit/lib/publicApi/webhookRoutes.server.test.ts tests/unit/supabase/public-api-webhook-contract-sql-source.test.ts"],"testFiles":["tests/unit/lib/publicApi/webhookRoutes.server.test.ts","tests/unit/supabase/public-api-webhook-contract-sql-source.test.ts"],"asserts":["delivery status, attempt count, exponential backoff and exhausted states are stable","manual redelivery requires webhooks:write and Idempotency-Key"]},"backwardsCompatibilityChecks":{"checklistItem":"backwards_compatibility_checks","commands":["npm run api:openapi:compat","npx vitest run tests/unit/scripts/public-api-openapi-lint.test.ts tests/unit/scripts/public-api-release-gates.test.ts"],"testFiles":["tests/unit/scripts/public-api-openapi-lint.test.ts","tests/unit/scripts/public-api-release-gates.test.ts"],"asserts":["stable operations, schemas, parameters, headers, scopes and reserved internal namespace are checked against the compatibility baseline","Deprecation/Sunset metadata is machine-checkable before any old-client migration"]},"clientFlowSmokeTests":{"checklistItem":"client_flow_smoke_tests","commands":["npm run api:sdks:compile","npx vitest run tests/unit/scripts/public-api-sdks.test.ts tests/unit/cli/moddingflow-cli.test.ts tests/unit/scripts/public-api-developer-experience.test.ts tests/unit/lib/publicApi/live-handler-response-shape-fixtures.test.ts"],"testFiles":["tests/unit/scripts/public-api-sdks.test.ts","tests/unit/cli/moddingflow-cli.test.ts","tests/unit/scripts/public-api-developer-experience.test.ts","tests/unit/lib/publicApi/live-handler-response-shape-fixtures.test.ts"],"asserts":["TypeScript, Python and C# SDK smoke clients call catalog, upload, publish, agent, OAuth and OIDC operations with stable URLs","runtime handler smoke covers catalog, install-plan, download, upload, publish, OAuth and Agent Gateway flows"]}},"restLayerVersioning":{"primaryStyle":"REST/JSON over HTTPS","resourceLifecycleScope":["files","versions","releaseLifecycle","webhooks"],"apiVersionInUrl":true,"canonicalVersionPrefix":"/v1","compatibilityVersionPrefix":"/api/v1","readEndpoints":["GET /v1/games","GET /v1/games/{game_slug}","GET /v1/categories","GET /v1/mods","GET /v1/mods/{mod_id}","GET /v1/mods/{mod_id}/versions","GET /v1/mods/{mod_id}/versions/{version_id}","GET /v1/mods/{mod_id}/dependencies","GET /v1/search","GET /v1/uploads/{upload_id}","GET /v1/agent/tools","GET /v1/webhook-subscriptions","GET /v1/webhook-deliveries"],"writeEndpoints":["POST /v1/mods","PATCH /v1/mods/{mod_id}","POST /v1/mods/{mod_id}/versions","PATCH /v1/mods/{mod_id}/versions/{version_id}","POST /v1/uploads","POST /v1/uploads/{upload_id}/parts","POST /v1/webhook-subscriptions","POST /v1/webhook-subscriptions/{subscription_id}/rotate-secret"],"lifecycleActions":["POST /v1/uploads/{upload_id}/complete","POST /v1/uploads/{upload_id}/abort","POST /v1/mods/{mod_id}/versions/{version_id}/publish","POST /v1/mods/{mod_id}/versions/{version_id}/rollback","POST /v1/downloads/{artifact_id}/resolve","POST /v1/downloads/{artifact_id}/fallback","POST /v1/install-plans:resolve","POST /v1/agent/tools:run","POST /v1/webhook-deliveries/{delivery_id}/redeliver"],"storageAbstraction":{"exposesProviderStorageKeys":false,"exposesSignedUrlSecrets":false,"publicContractDependsOnProvider":false,"clientAddressesFilesBy":["artifact_id","upload_id","download_session_id"]},"compatibleEvolution":{"newFields":"Additive response fields may ship in v1 when optional for old clients; new request fields must be optional, nullable, or have documented defaults.","newEndpoints":"New endpoints may be added under v1 when they do not replace an existing stable flow or become a hidden prerequisite for old clients.","enumStatusValues":"New enum/status values require tolerant client guidance, docs and SDK updates, and beta/experimental marking until the lifecycle semantics are stable.","breakingChangePath":"Breaking changes require a new major URL version or a documented deprecation path with migration notes and sunset metadata."},"compatibilityPolicy":{"backwardsCompatibility":"Stable v1 keeps existing public paths, operationIds, response fields, request fields, enum/status meanings, scopes, headers, errors, pagination semantics, and envelope compatibility fields working for old clients throughout the support window.","forbiddenWithoutVersioning":["remove_or_rename_public_operations","remove_or_rename_public_fields","change_existing_field_type_or_semantics","remove_or_rename_scopes_or_error_codes","change_cursor_idempotency_or_retry_semantics","remove_compatibility_namespace"],"fieldRemovalPolicy":"Removing or renaming a public field is forbidden in v1. Add a replacement field, keep the old alias readable, mark the old field deprecated, and remove it only in a new major version or after the documented deprecation path completes.","fieldTypePolicy":"Changing an existing field to an incompatible type or meaning is a breaking change. Use a new optional field or schema version instead while the original field keeps its old type and semantics.","additiveChanges":["optional_response_fields","optional_nullable_or_defaulted_request_fields","new_v1_endpoints_that_do_not_replace_existing_flows","new_enum_or_status_values_with_tolerant_client_guidance","new_optional_hash_algorithms","new_optional_webhook_event_kinds"],"deprecationHeader":{"name":"Deprecation","requiredFor":"Deprecated stable operations and deprecated capabilities that can be signaled at HTTP response time.","format":"RFC 9745 Structured Field date such as @1735689600.","openApiHeader":"#/components/headers/Deprecation"},"sunsetHeader":{"name":"Sunset","requiredFor":"Deprecated operations or capabilities with a planned removal or behavior shutdown date.","format":"RFC 8594 HTTP-date such as Wed, 01 Apr 2026 00:00:00 GMT.","openApiHeader":"#/components/headers/Sunset"},"migrationGuide":{"requiredForEveryDeprecation":true,"openApiExtensions":["x-migration-url","x-changelog-url","x-deprecation-date","x-sunset-date"],"linkHeaderRelation":"deprecation","minimumContents":["replacement_operation_or_field","client_detection_steps","safe_rollout_order","deadline","support_contact"]},"minimumNotice":{"days":90,"stableReleases":2,"sunsetMustNotPrecedeDeprecation":true},"oldClientContinuity":"During the notice window, old clients continue to receive the same status codes, field types, aliases, scopes, Problem Details codes, cursor semantics, and download/upload/webhook lifecycle behavior. Deprecated means migrate soon; it does not change runtime behavior by itself."},"uploadFlow":{"sessionCreation":"POST /v1/uploads creates an upload-session and returns direct-to-storage signed upload instructions.","largeFileTransport":"Large archive bytes are sent to object storage through a signed URL or multipart signed part URLs; the public API server only receives metadata, status checks, and lifecycle actions.","statusEndpoint":"GET /v1/uploads/{upload_id}","progressContract":"Multipart status includes provider-confirmed completed part numbers, ETags, byte counts, and percent for Website, CLI, and GitHub Action clients.","partsEndpoint":"POST /v1/uploads/{upload_id}/parts","commitEndpoint":"POST /v1/uploads/{upload_id}/complete","cancelEndpoint":"POST /v1/uploads/{upload_id}/abort","lifecycleStates":["created","uploading","uploaded","processing","ready","failed","aborted","expired"],"signedUrlExpiry":"Signed URLs are short-lived while upload sessions remain active for 48 hours. Every single PUT URL has a maximum 900-second lease that is row-lock checkpointed after signing and before response; cleanup is held past the persisted latest lease with a safety interval. Multipart clients refresh expired part URLs by requesting the same part numbers again; single PUT clients create a new upload-session if the signed URL expires before bytes are committed.","retryResume":"Writes require Idempotency-Key. Single PUT clients send every returned header, including the signed case-insensitive If-None-Match: * precondition; a conditional 412 is ambiguous-existing and proceeds to the same completion endpoint for authoritative server HEAD, immutable seal, and SHA-256 verification. Multipart clients poll provider-confirmed completed part ETags, request fresh URLs only for missing or expired parts, and complete with the full completed part list.","privateStaging":"Archive bytes remain in a private Cloudflare R2 staging bucket until completion, archive validation, and security processing succeed; publish never consumes an active or failed upload-session.","cleanup":"Expired or incomplete upload sessions are claimed by a bounded token-fenced service-role cleanup queue. Multipart cleanup aborts and relists exact-key uploads. Single cleanup first writes and provider-verifies a conditional zero-byte tombstone, durably waits past the latest persisted URL lease and at least 16 minutes, then exact-deletes and HEAD-verifies the staging key before database completion. Crash replay resumes the durable stage.","metadataOnlyEdits":"Metadata-only changes keep existing public IDs and do not require clients to re-upload the already accepted physical blob."},"fileIntegrity":{"canonicalHashAlgorithm":"sha256","canonicalHashField":"sha256","multiHashField":"hashes","parallelAlgorithms":["sha256"],"compatibilityAlgorithms":["sha1","md5"],"compatibilityOnly":"sha1 and md5 may appear only when legacy mod-manager ecosystems require them and must not replace SHA-256 verification for new clients.","uploadVerification":"POST /v1/uploads and POST /v1/uploads/{upload_id}/complete accept expectedSha256 or expectedHashes.sha256. Completion compares the stored/requested expected SHA-256 with object metadata or computed bytes, rejects sha256_mismatch on conflict, marks the upload rejected, and does not promote mismatched bytes.","downloadVerification":"Download and install-plan responses keep the legacy sha256 field and also expose hashes.sha256. Both fields cover the complete immutable stored-object bytes; neither an HTTP ETag nor an individual range response is a substitute for that digest.","downloadIntegrity":{"storedObjectDigest":{"algorithm":"sha256","fields":["sha256","hashes.sha256"],"scope":"complete_stored_object_bytes","sizeField":"size_bytes","appliesTo":["primary","fallback","install_plan"]},"representationValidator":{"field":"etag","header":"ETag","semantics":"opaque_transport_validator","cryptographicDigest":false,"scope":"single_transport_representation","crossProviderStable":false,"uses":["conditional_requests","range_resume","representation_change_detection"],"notFor":["content_integrity","artifact_identity","sha256_substitution"]},"rangeVerification":{"storedObjectDigestAvailablePerPartialRange":false,"verifyCompleteObjectAfterAssembly":true,"requiredChecks":["complete_byte_coverage","size_bytes_match","sha256_match"]},"standardHttpIntegrityFields":{"contentDigest":"not_guaranteed","reprDigest":"not_guaranteed","contractSource":"response_body","rfc":"https://www.rfc-editor.org/rfc/rfc9530","rationale":"The signed primary and fallback transports do not guarantee RFC 9530 Content-Digest or Repr-Digest headers. Clients verify the complete stored object with the response-body SHA-256 fields and use ETag only for one transport representation."},"representationTransformations":{"requiredContentEncoding":"identity_or_absent","transformedRepresentationsAllowed":false,"enforcement":"transport_incompatible_on_violation","policy":"Primary and fallback archive transports must preserve the complete stored-object bytes. A provider path that applies content coding or transcoding is incompatible until the API defines and verifies separate stored-object and HTTP representation digests."}},"additiveEvolution":"New hash algorithms are optional additive fields under hashes or file_hashes; removing sha256 or changing its meaning is a breaking change."},"asyncProcessing":{"jobModel":"Long-running operations expose an AsyncJob object with id, kind, resource, explicit terminal state, coarse completion progress, statusEndpoint, retry policy, durable timestamps, and a machine-readable reason when a worker rejects or fails the job.","longRunningOperations":["upload_processing","malware_security_scan","metadata_extraction","search_indexing","webhook_delivery"],"uploadProcessingFlow":"POST /v1/uploads/{upload_id}/complete commits uploaded bytes, records a durable upload_processing job, and may return processing before malware/security scan, metadata extraction, indexing hooks, or final file promotion finish.","statusEndpoint":"GET /v1/uploads/{upload_id}","statusEndpointPolicy":"The upload status endpoint returns lifecycle status plus job metadata until the upload reaches ready, failed, aborted, or expired. A future /v1/jobs/{job_id} endpoint can be added without replacing this stable polling path.","jobStatuses":["queued","processing","succeeded","rejected","failed","cancelled"],"nonTerminalStatuses":["queued","processing"],"terminalStatuses":["succeeded","rejected","failed","cancelled"],"machineReadableReasons":["archive_dangerous_entry","archive_duplicate_path","archive_encrypted_entry","archive_magic_mismatch","archive_mime_mismatch","archive_path_traversal","archive_symlink_entry","zip_bomb_guard","sha256_mismatch","malware_detected","worker_failed","indexing_failed"],"progressSemantics":"progress is a truthful state-derived job completion unit, not an invented scan percentage: non-terminal jobs are 0/1 and terminal jobs are 1/1. Clients use status, timestamps, and retry metadata for in-flight diagnostics.","timestampSemantics":"createdAt and updatedAt come from the durable job row; startedAt is the first/current worker claim time and may be null before a claim; completedAt is required by runtime for terminal jobs and null for non-terminal jobs.","failureReasonSemantics":"reason is always present and nullable. Rejected, failed, and cancelled jobs expose a stable allow-listed machine code; unrecognized provider prose is never forwarded and maps to av_scan_rejected or worker_failed.","retryPolicy":"Transient worker failures remain publicly non-terminal while attempts remain. retry exposes attempt, maxAttempts, nextAttemptAt, retryable, strategy, and Retry-After guidance; rejected or exhausted jobs are terminal and expose a stable reason code.","connectionPolicy":"Public clients must never keep a long HTTP connection open while upload processing, scan, metadata extraction, indexing, or webhook delivery completes; they poll the status endpoint using job.status and retry hints."},"consistencyModel":{"checklistItem":"PR-F2.6.4","globalRule":"A successful mutation response is the committed private no-store acknowledgement for that operation. It does not create a universal cache-bypass or cross-resource strong-consistency guarantee; clients follow the resource-specific polling, revalidation, and re-resolve rules below.","numericCatalogConvergenceTrackedBy":"PR-F2.6.3","deployedWebhookEnablementTrackedBy":"PR-F8.14","resources":{"version":{"writeAcknowledgement":"committed_private_no_store","publicCatalogVisibility":"eventual_shared_cache","operationIds":["listModVersions","createModVersion","getModVersion","updateModVersion","publishModVersion","rollbackModVersion"],"clientAction":"Use the successful mutation response as the authoritative committed result. Anonymous catalog/version GET representations can remain stale under their documented shared-cache policy, so revalidate with ETag and never use catalog visibility as the write acknowledgement."},"artifact":{"catalogExpansionBarrier":"ready_clean_and_published","directReadEligibility":"ready_and_download_target_eligible_approved_or_published","cachedVersionExpansion":"eventual_shared_cache","pointInTimeReads":"private_no_store_resolve_and_install_plan","operationIds":["listModVersions","getModVersion","resolveDownload","resolveInstallPlan"],"clientAction":"Cached version expansion is authoritative only after clean ready-and-published eligibility succeeds and remains eventual under shared cache. Direct download resolve and install-plan resolve are private point-in-time reads over an eligible approved-or-published download target; resolve them again after relevant state changes."},"uploadStatus":{"databaseState":"current_at_request_time","workerTransitions":"eventual_poll_until_terminal","providerProgress":"authoritative_only_when_available_true_unknown_when_false","operationIds":["createUploadSession","getUploadSession","completeUploadSession","abortUploadSession"],"clientAction":"Poll GET /v1/uploads/{upload_id} and follow job.statusEndpoint plus retry hints until a terminal state. progress.available=false means provider progress is currently unknown; it never means zero bytes or lost accepted parts."},"installPlan":{"snapshot":"deterministic_request_time_snapshot","cachePolicy":"private_no_store_with_etag","laterChanges":"not_live_updated_resolve_again","operationIds":["resolveInstallPlan"],"clientAction":"The returned plan and ETag describe one deterministic request-time database snapshot. A plan is not a live subscription; resolve it again after a version, artifact, dependency, entitlement, or publication change."},"webhooks":{"managementWriteAcknowledgement":"committed_private_no_store","delivery":"eventual_at_least_once_replayable_unordered","featureGate":"no_delivery_when_management_or_delivery_disabled","operationIds":["listWebhookSubscriptions","createWebhookSubscription","rotateWebhookSubscriptionSecret","listWebhookDeliveries","redeliverWebhookDelivery"],"clientAction":"A committed API mutation is not acknowledged by webhook arrival. When both webhook feature gates are enabled, receivers return 2xx quickly, deduplicate by event_id, tolerate replay and reordering, and fetch latest state. When either gate is disabled, no delivery is promised."}}},"compensationModel":{"checklistItems":["PR-F2.6.6","PR-F2.6.7"],"globalRule":"The upload, trust, publication, and notification pipeline is a fail-closed compensating state machine. A generation-fenced once-only byte-budget checkpoint precedes an atomic database reservation that commits session and file intent with the upload.accepted audit before provider work; replay between those checkpoints reuses the same budget result. Every single PUT is signed and returned with If-None-Match: *, and its maximum 900-second URL lease is row-lock checkpointed before response. Client staging remains mutable, so provider completion is followed by a persisted session-level seal claim, an ETag-conditional copy into a unique server-only immutable seal, and one atomic pending-hash scan checkpoint with a unique job. A token-fenced worker hashes and scans only that seal; each ready, quarantine, or rejection transition commits its audit and canonical event atomically. Before normal claims or provider configuration, a bounded database-only sweep recovers stale max-attempt claims: coherent rows atomically enter canonical manual review and quarantine, while incoherent rows terminal-fail only the exact job for visible manual intervention, so neither class can remain processing forever or rerun provider work. Same-SHA final promotion is serialized by a durable claim and a conditional Cloudflare destination copy; fresh duplicates wait or adopt without consuming normal scan attempts, while exponential polling is bounded to a 15-minute claim window, the first two durable contention yields atomically requeue with database-owned 30-to-60-second jitter, and the third atomically opens manual review in quarantine. An accessible attested content-addressed final blob is followed by one atomic ready binding, and publication and its outbox share one database transaction. Token-fenced single cleanup first writes and provider-verifies a conditional zero-byte tombstone, holds it past the persisted latest URL lease and at least 16 minutes, then exact-deletes and HEAD-verifies staging; multipart cleanup aborts and relists exact-key uploads. Every cleanup stage is durable and replayable, quarantine bytes are retained, and any unbound final copy stays non-ready and non-publishable until reference-aware garbage collection.","crossProviderTransaction":"none_compensating_state_machine_only","publishPrecondition":"accessible_attested_blob_with_atomic_ready_binding","idempotencyGeneration":"request_hash_scoped_generation_with_cas_execution_claim","cleanupNotBefore":"after_terminal_delay_and_all_signed_upload_urls_expire","numericFreshnessTrackedBy":"PR-F2.6.3","residualOrphanGcTrackedBy":"PR-F3.13","deployedWebhookEnablementTrackedBy":"PR-F8.14","providerControlPlane":{"desiredCorsRequiredRequestHeader":"If-None-Match","desiredCorsPolicy":"allow_the_signed_if_none_match_request_header_for_approved_origins","liveCorsEvidence":"blocked_not_verified","liveCorsBlocker":"cloudflare_management_token_policy_floor_and_approved_origins_required","liveVerifierBlockers":["CLOUDFLARE_API_TOKEN","MODDINGFLOW_R2_FINAL_LOCK_MIN_SECONDS","MODDINGFLOW_R2_ALLOWED_BROWSER_ORIGINS"],"deployedCorsLifecycleAndLockEvidence":"blocked_not_verified","objectLevelCompensationProbe":"verified_conditional_copy_and_late_put_tombstone"},"stages":{"uploadReservation":{"ordering":"once_only_generation_budget_checkpoint_then_atomic_session_file_and_accepted_audit_reservation","requestIdentity":"actor_key_generation_and_normalized_request_hash","replay":"exact_replay_returns_same_reservation_changed_payload_conflicts","byteBudget":"durable_once_per_generation_and_replayed_before_session_reservation","audit":"upload_accepted_exactly_once_inside_same_database_transaction","failureRecovery":"replay_same_generation_budget_then_atomic_session_file_and_audit_reservation","retention":"delete_only_after_idempotency_replay_window_plus_safety_margin","clientAction":"Create retries reuse the same Idempotency-Key and normalized body. A once-only generation byte-budget checkpoint is followed by an atomic session, file, and upload.accepted reservation; a lost response between them replays the same budget result before converging on that reservation. Changed payload conflicts, and reuse after the retention window creates a new generation."},"providerCommit":{"ordering":"provider_commit_then_persisted_seal_claim_then_etag_conditional_copy_then_atomic_pending_hash_checkpoint","singlePutPrecondition":"signed_and_returned_if_none_match_star_required","singlePutConditional412":"ambiguous_existing_then_complete_server_head_seal_and_hash_authoritative","singlePutUrlCheckpoint":"row_locked_after_sign_before_response","singlePutUrlMaxTtlSeconds":900,"singlePutUrlLeaseSafety":"cleanup_waits_for_persisted_latest_lease_and_at_least_16_minutes","singlePutAbortRace":"abort_winning_checkpoint_rejects_signed_url_response","sealSource":"exact_committed_staging_etag","sealIdentity":"unique_server_only_write_once_key_per_claim","checkpoint":"atomic_idempotent_session_file_seal_and_unique_scan_job_transition","hashState":"pending_until_token_fenced_worker_attestation","concurrency":"one_active_session_seal_executor_with_token_fenced_stale_takeover","replay":"exact_replay_returns_existing_checkpoint","failureRecovery":"head_validate_same_seal_after_ambiguous_copy_or_fenced_takeover_and_cleanup","clientAction":"For a single PUT, send every returned header including If-None-Match: *. Treat a conditional 412 as ambiguous-existing and call the same completion endpoint; server-side HEAD, immutable sealing, and SHA-256 verification are authoritative, so do not overwrite or start a second transfer. After any provider completion attempt, retry the same completion operation with the same Idempotency-Key while upload status remains uploaded or the outcome is ambiguous. The server serializes completion per session, copies the exact committed staging ETag into an immutable server-only seal, and atomically persists its pending-hash checkpoint and unique scan job. Once that checkpoint and job exist, polling upload status is sufficient; no cross-provider transaction or successful publication is implied by an ambiguous response."},"scanWorker":{"ordering":"claim_then_hash_attest_validate_av_promote_and_atomic_terminal_commit","claimFencing":"lease_heartbeat_and_token_required_for_every_mutation","input":"persisted_immutable_seal_only_never_client_staging","attestation":"database_timestamped_sha256_exact_replay_or_atomic_mismatch_rejection","terminalCommit":"session_file_job_audit_and_canonical_event_same_database_transaction","staleWorkerPolicy":"stale_or_wrong_token_cannot_mutate_or_override_terminal_verdict","responseLoss":"reload_and_validate_exact_terminal_envelope_before_accepting_commit","exhaustedClaimReconciliation":"bounded_database_only_sweep_before_provider_config_and_normal_claims","exhaustedClaimCoherentOutcome":"atomic_canonical_manual_review_quarantine_without_provider_rerun","exhaustedClaimIncoherentOutcome":"exact_token_terminal_failed_manual_intervention_required","clientAction":"Queued and processing scans are asynchronous. Poll the upload status endpoint; a worker hashes, validates, scans, and promotes only the persisted immutable seal, and an ambiguous worker response is accepted only after the exact committed terminal envelope is reloaded and validated. Before provider configuration or normal claims, a bounded database-only sweep recovers stale max-attempt processing claims without rerunning scan or provider work: coherent state atomically enters canonical manual review and quarantine, while incoherent state terminal-fails only the exact job for visible manual intervention instead of remaining stuck forever."},"artifactPromotion":{"ordering":"attested_content_addressed_final_copy_then_atomic_ready_audit_and_event_binding","contentIdentity":"immutable_sha256_addressed_blob","concurrency":"durable_sha_bucket_key_claim_serializes_copy_or_adoption","canonicalContentType":"claim_owned_canonical_mime_and_exact_duplicate_adoption","destinationPrecondition":"cloudflare_cf_copy_destination_if_none_match_star","readyPrecondition":"final_blob_accessible_and_attested","copyResponseLoss":"head_validate_sha256_metadata_size_canonical_content_type_and_nonblank_etag_then_persist_observed_etag","freshDuplicate":"waits_or_adopts_with_bounded_three_yield_budget_without_consuming_normal_scan_attempts","contentionPoll":"exponential_1_to_30_seconds_with_15_minute_claim_window","contentionYieldBudget":3,"contentionYield":"token_fenced_atomic_requeue_with_database_owned_30_to_60_second_jitter_and_attempt_restore","contentionExhaustion":"third_yield_atomically_commits_manual_review_quarantine","binding":"atomic_idempotent_blob_file_session_audit_and_file_ready_event_binding","failureRecovery":"replay_content_addressed_copy_or_atomic_binding","clientAction":"A content-addressed final copy is not ready metadata. A durable SHA/bucket/key claim serializes first copy and duplicate adoption, owns the canonical MIME, and uses Cloudflare's cf-copy-destination-if-none-match: * destination precondition. A fresh duplicate waits or adopts the exact ready blob without consuming a normal scan attempt. Polling is exponential and bounded to 15 minutes per claim; the first two durable token-fenced yields atomically requeue with database-owned 30-to-60-second jitter, while the third atomically creates manual review and quarantine instead of retrying forever. The immutable final blob must be accessible and match its seal attestation before one atomic binding can make the file and upload ready while committing its audit and file.ready event; an ambiguous copy response is recovered only by exact final-object HEAD validation including a nonblank provider ETag."},"publish":{"ordering":"transactional_publish_and_outbox","precondition":"accessible_attested_blob_bound_ready","outbox":"same_database_transaction_as_publication","delivery":"asynchronous_after_outbox_commit","clientAction":"Publication and its durable outbox event commit in one database transaction and only after the accessible attested blob is atomically bound ready. A delivery attempt is asynchronous and is never the publication acknowledgement."},"trustWebhook":{"ordering":"atomic_terminal_state_audit_and_canonical_event","eventIdentity":"stable_dedupe_key_for_logical_trust_event","duplicatePolicy":"exact_replay_returns_existing_enqueue_changed_envelope_conflicts","terminalStateCoupling":"same_database_transaction_as_terminal_state","recovery":"bounded_legacy_gap_validation_and_repair_respects_active_claims","featureGate":"enqueue_contract_does_not_assert_deployed_delivery","clientAction":"Each new ready, quarantine, or rejection transition commits its audit and canonical event in the same database transaction with a deterministic dedupe identity. Exact replay validates and returns the existing event, a changed envelope conflicts, and bounded reconciliation is limited to explicit legacy gaps while respecting active claim ownership. Deployed webhook delivery is not implied."},"lostResponseReplay":{"ordering":"generation_scoped_cas_claim_then_exact_committed_envelope_recovery","committedResponse":"same_key_same_body_returns_committed_result","sideEffects":"no_duplicate_budget_audit_job_terminal_event_or_outbox_effect","ambiguousOutcome":"same_generation_key_and_body_replay_or_exact_status_inspection","terminalRecovery":"exact_state_audit_event_and_job_envelope_validation","clientAction":"For upload, trust, and publish operations with replayable results, reuse the same Idempotency-Key and normalized body after a lost response. Replay reacquires only the same generation CAS claim and returns the exact committed envelope without duplicate budget, audit, job, terminal-event, or outbox effects; a changed envelope or competing verdict fails closed."},"orphanCleanup":{"ordering":"token_fenced_single_tombstone_stage_then_exact_delete_or_multipart_abort_relist_then_separate_final_copy_gc","trigger":"terminal_or_expired_uncheckpointed_upload_with_no_active_seal_or_scan_claim","semantics":"bounded_claimed_idempotent_durable_stage_replay_and_result_cas","stagingCleanup":"single_two_stage_tombstone_then_exact_delete_and_head_verify_plus_every_claimed_seal_key","singlePutStageOne":"conditional_zero_byte_tombstone_write_and_provider_verification","singlePutTombstoneProof":"provider_etag_last_modified_metadata_token_and_zero_size","singlePutTombstoneHold":"persisted_latest_url_lease_and_at_least_16_minutes","singlePutStageTwo":"exact_delete_then_head_not_found_then_database_completion","crashReplay":"durable_stage_checkpoint_replays_current_stage","multipartCleanup":"abort_and_relist_exact_key_until_no_uploads_remain","quarantinePolicy":"retain_quarantine_bytes_and_exclude_from_cleanup","reservationRetention":"purge_only_after_idempotency_replay_window_plus_safety_margin","finalCopyHandling":"retained_non_ready_non_publishable_until_reference_aware_gc","safety":"unbound_orphans_never_satisfy_ready_or_publish_preconditions","clientAction":"For single PUT staging, a token-fenced worker first conditionally writes a zero-byte tombstone and verifies provider ETag, LastModified, metadata token, and size. It durably holds that fence until the persisted latest signed-URL lease and at least 16 minutes have elapsed, then exact-deletes and HEAD-verifies absence before database completion. Abort-winning URL checkpoints reject the URL response, and a crash resumes the current durable stage. Multipart cleanup aborts and relists exact-key uploads until none remain. Every claimed seal key is deleted and HEAD-verified, result commit requires the current claim token, quarantine bytes are retained, and any residual unbound final copy remains non-ready and non-publishable; reference-aware deletion stays tracked separately by PR-F3.13."}}},"downloadFlow":{"grantCreation":"POST /v1/downloads/{artifact_id}/resolve creates a durable actor-bound download job and a short-lived download-grant identified by download_job.id and download_session_id instead of returning a permanent public file link.","grantLifetime":"download_session_id, expires_at, expires_in, and refresh_after_seconds define the usable grant window. Clients refresh by resolving the same artifact again before expires_at instead of storing signed URLs.","accessControls":"The grant is bound to the artifact, actor user when authenticated, client/app identity when present, required files:download or delegated download scope, entitlement checks, route rate limits, and byte-budget abuse controls.","signedUrlResolution":"The resolve response can mint primary_url, head_url, fallback_endpoint, and optional fallback_url transport artifacts from the grant. The GET URL supports byte ranges and RFC conditional headers; HEAD uses its own operation-scoped signature. The /fallback action requires downloadSessionId and may mint a provider-specific signed URL only while the grant remains active.","grantErrorPolicy":"Missing artifacts return 404, missing auth returns 401, unavailable scope/entitlement/rate-limit cases return 403 or 429, and expired or no-longer-usable grants return 410 with a machine-readable code such as download_session_expired. On download 429, persist download_job.id, grant_id, bytes_received, etag and next_attempt_at, honor Retry-After, and resend job_id plus monotonic progress; repeated limits do not mint another grant or provider effect.","storageProviderIndependence":"primary_url, fallback_url, provider names, CDN routing, and object-storage signing details are transport details and may change without breaking clients that persist artifact_id and download_session_id.","urlMutationCompatibility":"Clients must treat signed URL hosts, query strings, and headers as opaque. Refreshing the grant or replacing a CDN provider is compatible as long as artifact_id, download_session_id, hashes, size, ETag field and validator semantics, HEAD/Range/conditional capabilities, and documented errors remain stable. An ETag value is scoped to one transport representation and may change after refresh or provider replacement."},"experimentalEndpoints":{"allowed":true,"allowedLocations":["OpenAPI operations marked x-moddingflow-stage=beta or x-moddingflow-stage=experimental","future /v1/beta/* paths","website/internal routes absent from the stable public OpenAPI contract"],"notStablePrerequisite":true},"changelogMigrationNotes":{"format":"Every compatible API release gets a changelog entry; every deprecation gets migration notes, x-deprecation-date, x-sunset-date, x-migration-url, and x-changelog-url.","minimumDeprecationDays":90,"migrationNotesRequiredForBreakingChange":true},"webhookFlow":{"subscriptionModel":"Webhook subscriptions are owner-scoped public API resources with id, target_url, event_kinds, status, signing secret metadata, created_at, updated_at, and disabled_at. Clients manage them with webhooks:read or webhooks:write and never see another user subscription.","eventNames":["mod.updated","version.published","file.ready","file.failed","upload.completed","scan.completed","token.changed","app.changed"],"signatureAlgorithm":"hmac-sha256 over `${timestamp}.${raw_body}` using the subscription signing secret; compare signatures in constant time and reject stale timestamps.","signatureHeader":"X-Moddingflow-Signature with value `t=<unix_seconds>,v1=<base64url_hmac_sha256>` plus X-Moddingflow-Timestamp and X-Moddingflow-Delivery-Id convenience headers.","deliveryIdField":"delivery_id is an opaque UUID for one durable delivery row; event_id remains stable across linked manual redeliveries. Receivers dedupe business effects by event_id and use delivery_id for attempt tracing.","idempotencyExpectations":"Webhook receivers must make side effects idempotent by event_id, retain delivery_id for diagnostics, tolerate duplicates and out-of-order events, then fetch the latest resource state before applying mutable state.","retryRedeliveryPolicy":"Automatic delivery retries use exponential backoff with jitter for network errors, 408, 425, 429 and 5xx responses until exhausted. Manual redelivery creates a new delivery_id linked by redelivery_of_delivery_id and root_delivery_id while preserving event_id.","permanentFailurePolicy":"After the maximum attempt count or a non-retryable receiver response, delivery_status becomes exhausted or failed, next_attempt_at is cleared, the last status/error is retained, and repeated permanent failures can disable the subscription until the owner fixes the endpoint.","endpoints":{"subscriptions":["GET /v1/webhook-subscriptions","POST /v1/webhook-subscriptions","POST /v1/webhook-subscriptions/{subscription_id}/rotate-secret"],"deliveries":["GET /v1/webhook-deliveries","POST /v1/webhook-deliveries/{delivery_id}/redeliver"]},"retryStatuses":["queued","delivering","delivered","failed","exhausted","cancelled"]}},"publicIdPolicy":{"resourceIds":"UUID strings are the stable public identifiers for API resources.","slugs":"Slugs are human-readable aliases and may change; clients must persist UUID ids.","traceIds":"Trace and request ids are opaque strings emitted per request."},"pagination":{"style":"cursor-first","cursorParameter":"cursor","limitParameter":"limit","defaultLimit":25,"maxLimit":100,"stableOrdering":"updated_at desc, id asc unless an endpoint documents a narrower sort.","cursorFormat":"Opaque v1. base64url JSON keyset cursor returned only by the server. It includes the active sort key, timestamp tie-breaker, and UUID; clients must echo it without parsing or editing it.","contextBinding":"Reusing a cursor with a different resource, normalized filter set, or sort is rejected with invalid_input instead of silently restarting at page one.","snapshotStability":"Rows created or published after the first page snapshot remain outside that cursor's result window.","mutationStability":"Pages advance from the last returned keyset tuple, so new rows inserted before that tuple between requests do not move the already-read window or create offset drift.","offsetPolicy":"Offset/page-number pagination is forbidden for mutable public API lists; compatibility aliases must be translated to cursor-first list calls before database work.","appliesTo":["GET /v1/mods","GET /v1/search","GET /v1/mods/{mod_id}/versions","GET /v1/webhook-deliveries"],"filterParameters":["game_slug","q","game_version","loader","category"],"resourceFilters":{"mods":["game_slug","q","game_version","loader","category"],"versions":["mod_id","release_channel","game_version","loader","published_after","published_before"],"files":["mod_id","version_id","artifact_id","file_kind","status","sha256"],"uploadSessions":["status","strategy","file_id","created_after","created_before"],"downloadGrants":["artifact_id","status","expires_after","expires_before"],"webhookDeliveries":["event_kind","delivery_status","created_after","next_attempt_before"]},"sortParameter":"sort","sortValues":["relevance","latest","trending","downloads"],"listSortDefault":"latest","searchSortDefault":"relevance","runtimeValidation":"Runtime handlers validate cursor length, 1..100 limit, supported sort values, query length, and catalog/search filters before RPC work.","cursorContextValidation":"Runtime handlers validate cursor payload shape plus resource, sort, and normalized-filter context binding before RPC work."},"errors":{"format":"RFC 9457 Problem Details for HTTP APIs","mediaType":"application/problem+json","typeUriBase":"https://api.moddingflow.com/problems/","requiredMembers":["type","title","status","detail","instance"],"envelope":"{ type, title, status, detail, instance, code, retryable, docs_slug, error, optional retry_after_seconds/quota_bucket/required_scopes/fields }","compatibilityBridge":"application/problem+json is the canonical error media type. Top-level ok/code, machine_code/http_status, and nested error are preserved as extension members for existing SDKs and site envelopes.","globalRegistry":"#/x-moddingflow-error-registry","unknownCodeBehavior":"Preserve unknown code, payload, numeric status, headers, request_id, and trace_id; fall back to generic status handling.","httpStatusMapping":{"invalid_request":400,"unauthorized":401,"forbidden":403,"not_found":404,"conflict":409,"precondition_failed":412,"precondition_required":428,"validation_failed":422,"rate_limited":429,"internal_error":500}},"idempotency":{"header":"Idempotency-Key","requiredFor":["POST /v1/uploads","POST /v1/uploads/{upload_id}/parts","POST /v1/uploads/{upload_id}/complete","POST /v1/uploads/{upload_id}/abort","POST /v1/mods","PATCH /v1/mods/{mod_id}","POST /v1/mods/{mod_id}/versions","PATCH /v1/mods/{mod_id}/versions/{version_id}","POST /v1/mods/{mod_id}/versions/{version_id}/publish","POST /v1/mods/{mod_id}/versions/{version_id}/rollback","POST /v1/api-keys","POST /v1/api-keys/{api_key_id}/rotate","POST /v1/webhook-subscriptions","POST /v1/webhook-subscriptions/{subscription_id}/rotate-secret","POST /v1/webhook-deliveries/{delivery_id}/redeliver"],"acceptedFor":["POST /v1/downloads/{artifact_id}/resolve","POST /v1/downloads/{artifact_id}/fallback","POST /v1/install-plans:resolve","POST /v1/agent/tools:run"],"retention":"At least 24 hours per authenticated client and operation family.","scope":"Authenticated actor + operation/resource identity + Idempotency-Key + normalized request hash.","classificationValues":["naturally_idempotent","idempotency_key_protected","optimistic_concurrency_protected","explicitly_non_retryable_until_result"],"operationClassifications":{"createModDraft":"idempotency_key_protected","updateModDraft":"optimistic_concurrency_protected","createModVersion":"idempotency_key_protected","updateModVersion":"optimistic_concurrency_protected","createUploadSession":"idempotency_key_protected","validateUploadRequest":"naturally_idempotent","resolveDownload":"explicitly_non_retryable_until_result","resolveDownloadFallback":"explicitly_non_retryable_until_result","resolveInstallPlan":"naturally_idempotent","createApiKey":"idempotency_key_protected","revokeApiKey":"naturally_idempotent","rotateApiKey":"idempotency_key_protected","setModTeamMember":"naturally_idempotent","removeModTeamMember":"naturally_idempotent","createDeviceAuthorization":"explicitly_non_retryable_until_result","exchangeOAuthToken":"explicitly_non_retryable_until_result","revokeOAuthToken":"naturally_idempotent","introspectOAuthToken":"naturally_idempotent","signUploadParts":"idempotency_key_protected","completeUploadSession":"idempotency_key_protected","abortUploadSession":"idempotency_key_protected","publishModVersion":"idempotency_key_protected","rollbackModVersion":"idempotency_key_protected","runAgentGatewayTool":"explicitly_non_retryable_until_result","createWebhookSubscription":"idempotency_key_protected","rotateWebhookSubscriptionSecret":"idempotency_key_protected","redeliverWebhookDelivery":"idempotency_key_protected"},"oneTimeSecretReplay":{"operationIds":["createApiKey","rotateApiKey"],"staleClaimPolicy":"fail_closed_no_reclaim","sameKeySameBody":"The credential mutation is never repeated. idempotency_replay_unavailable omits operation-shaped committed metadata because the one-time raw secret is not persisted and the registry allows no response extensions; clients inspect or revoke the key through the API.","sameKeyDifferentBodyCode":"idempotency_conflict","rawSecretPersistence":false}},"preconditions":{"readResponsesEmit":"ETag","mutatingUpdatesRequire":"If-Match","deleteRequires":"If-Match","strongWriteValidators":{"operationIds":["updateModDraft","updateModVersion"],"responseHeader":"ETag","missingStatus":428,"mismatchStatus":412,"weakValidatorsAccepted":false,"wildcardAccepted":false,"idempotencyFingerprintIncludes":["body","If-Match"],"clientRecovery":"Fetch an authenticated detail representation, rebase the intended change, and retry with the latest strong ETag plus a new Idempotency-Key. Never blind-retry 412."},"notYetPublishedOperations":["DELETE /v1/mods/{mod_id}","DELETE /v1/mods/{mod_id}/versions/{version_id}"]},"catalogCaching":{"operationIds":["listGames","getGame","listMods","getMod","listModVersions","getModVersion","listModDependencies","listCatalogCategories","searchCatalog"],"anonymous":{"cacheControl":"public, max-age=0, s-maxage=0, no-cache, must-revalidate","validator":"weak ETag over the exact JSON representation","vary":["Accept-Encoding","Authorization","Cookie"]},"conditionalRequest":{"header":"If-None-Match","matchStatus":304,"matchBody":"empty","comparison":"weak"},"identityBound":{"requestHeaders":["Authorization","Cookie"],"cacheControl":"private, no-store, no-cache, must-revalidate, max-age=0","publicValidatorEmitted":false}},"domainModel":{"forumTablesRemainPrimaryWriteModel":true,"canonicalLayer":"Supabase canonical views plus Rust-owned public DTOs. The website keeps forum/topic write paths during dual-read rollout.","stableIdBridge":"public_api_resource_ids assigns UUIDs to legacy forum/storage source rows before they are exposed as public resources.","immutableEntities":{"game":{"schema":"PublicGame","stableFields":["id","slug","title","is_enabled","created_at","updated_at"],"relationships":["has many mods through PublicMod.game_id","is curated by the system catalog owner"],"identity":"PublicGame.id is the durable public identity. slug, localized title, icon, and sort order are mutable catalog aliases."},"mod":{"schema":"PublicMod","stableFields":["id","game_id","title","summary","visibility","access_tier","created_at","updated_at"],"relationships":["has many versions through PublicModVersion.mod_id","has many dependencies through PublicModDependency.mod_id"],"identity":"PublicMod.id is the durable public identity. slug, localized_slugs, title, and summary are mutable presentation fields."},"version":{"schema":"PublicModVersion","stableFields":["id","mod_id","version","semantic_version","release_channel","artifact_ids","artifacts","primary_artifact_id","published_at","updated_at"],"relationships":["belongs to mod through mod_id","binds logical files through artifact_ids and primary_artifact_id; complete managed metadata is expanded through artifacts when current scan and download-target evidence exists"],"identity":"PublicModVersion.id is stable across metadata edits. The human version string is display and compatibility data, not the key."},"logicalFile":{"schema":"PublicModArtifact","stableFields":["id","mod_id","version_id","file_kind","label","original_filename","content_type","game_version_key","loader_key","size_bytes","hashes","artifact_source","status","scan_status","download_metadata"],"relationships":["belongs to version through version_id","may point at a physical blob when artifact_source is r2_blob","legacy rows without a blob stay typed as legacy_external_url","download_metadata resolves through the artifact-bound /v1 download-grant flow without exposing provider locations"],"responseContract":{"expandedField":"PublicModVersion.artifacts","completeSchema":"PublicModArtifactDetails","scanSource":"upload_sessions.public_api_scan_status matched by both file_id and final_blob_id","downloadEligibilitySource":"public_api_mod_manager_download_targets","legacyRule":"Omit expanded artifacts when current managed size, SHA-256, AV-scan, and resolve eligibility evidence is unavailable; artifact_ids remain the stable linkage."},"identity":"PublicModArtifact.id is the logical file identity exposed to clients. filename and storage object paths are not public identities."},"physicalBlob":{"schema":"PublicBlob","stableFields":["id","sha256","size_bytes","content_type","created_at"],"relationships":["referenced by logical files through mod_artifacts/blob linkage","can be reused by deduplicated logical files"],"identity":"The physical blob is the immutable stored object. sha256 verifies bytes, but clients address files through artifact/download resources rather than treating a single hash or filename as the only key."},"uploadSession":{"schema":"UploadSessionResponse","stableFields":["sessionId","strategy","fileId","expiresIn","partsEndpoint","completeEndpoint","abortEndpoint"],"relationships":["creates or updates one logical file","promotes an accepted upload to a physical blob on complete"],"identity":"sessionId is the upload-session lifecycle identity. Signed URLs, staging keys, and original filenames are temporary implementation details."},"downloadGrant":{"schema":"DownloadResolveResponse","stableFields":["artifact_id","download_job","download_session_id","expires_at","expires_in","refresh_after_seconds","sha256","hashes","size_bytes","head_url","accept_ranges","conditional_headers","range_supported","access","download_grant"],"relationships":["authorizes temporary access to one logical file","binds the grant to the actor user/client authorization and scope checks","can mint provider-specific signed URLs without exposing storage identity"],"identity":"download_job.id is the durable retry identity and download_session_id/download_job.grant_id is the stable grant identity for one job. expires_at/expires_in bound the signed transport lifetime; primary_url and fallback_url are short-lived transport artifacts."},"user":{"schema":"PublicUser","stableFields":["id","username","display_name","state","created_at","updated_at"],"relationships":["owns Teams and authored Mods without exposing auth or email fields","keeps the auth.users UUID across username, login, and profile URL changes"],"identity":"PublicUser.id is the durable auth.users UUID. Username, login, email, and profile URLs are mutable aliases and are never accepted as identity replacements."},"team":{"schema":"PublicTeam","stableFields":["id","mod_id","owner_user_id","name","state","created_at","updated_at"],"relationships":["belongs to one owner User","delegates bounded Mod capabilities through membership roles"],"identity":"PublicTeam.id is independent from mod_id, team name, and membership. Renaming or transferring ownership never changes the Team UUID."},"dependency":{"schema":"PublicModDependency","stableFields":["id","mod_id","target_mod_id","dependency_kind","relation","created_at"],"relationships":["belongs to one Mod","may target another Mod UUID or an external requirement alias"],"identity":"PublicModDependency.id is durable across label, URL, note, relation-display, and target-alias edits."},"job":{"schema":"PublicJob","stableFields":["id","resource_id","kind","state","attempt_count","created_at","updated_at"],"relationships":["belongs to its initiating User or resource","worker leases and attempts never replace the Job UUID"],"identity":"PublicJob.id is the stable diagnostics and replay identity. Worker token, queue position, scanner, and retry attempt are mutable execution metadata."},"webhookSubscription":{"schema":"WebhookSubscription","stableFields":["id","target_url","event_kinds","status","created_at","updated_at"],"relationships":["belongs to the creating User/client authorization","has many WebhookDelivery resources"],"identity":"WebhookSubscription.id survives target URL, name, filter, status, and secret rotation changes. Deleted subscriptions retain a 410 tombstone."},"token":{"schema":"OAuthTokenResponse + PersonalAccessTokenMetadata","stableFields":["access_token","token_type","expires_in","scope","id","display_prefix","scopes","last_used_at","expires_at","revoked_at"],"relationships":["belongs to a user/client authorization grant","authorizes scoped access without exposing token_hash or service-role secrets"],"identity":"Bearer tokens are opaque secrets. PAT metadata uses id/display_prefix for management while token_hash remains private."},"webhookDelivery":{"schema":"WebhookDelivery","stableFields":["id","delivery_id","event_kind","delivery_status","attempt_count","next_attempt_at","created_at"],"relationships":["records asynchronous delivery for upload, publish, and future subscription events","links back to mod/version/file lifecycle resources when applicable"],"identity":"Webhook delivery rows have their own delivery id and retry state. Payload event names and signatures must remain stable once public webhook endpoints are added."},"identityIndependence":{"forbiddenPrimaryKeys":["slug","localized_slugs","filename","original_filename","sha256","etag"],"slugPolicy":"Slugs and localized slugs are mutable aliases for human routing and search.","filenamePolicy":"Filenames and original filenames are display/download hints and must not identify a public resource.","hashPolicy":"A hash verifies bytes and may support dedupe/integrity checks, but no public entity is addressed only by one hash algorithm. New clients use sha256 as canonical and treat sha1/md5 or future hashes as optional compatibility metadata."}},"addressingPolicy":{"opaqueIds":{"mod":{"field":"id","schema":"ModId","format":"uuid","source":"public_api_resource_ids"},"game":{"field":"id","schema":"GameId","format":"uuid","source":"public_api_resource_ids"},"version":{"field":"id","schema":"VersionId","format":"uuid","source":"public_api_resource_ids"},"logicalFile":{"field":"id","schema":"ArtifactId","format":"uuid","source":"public_api_resource_ids"},"physicalBlob":{"field":"id","schema":"PublicBlob.id","format":"uuid","source":"storage_blobs.id"},"uploadSession":{"field":"sessionId","schema":"UploadSessionResponse.sessionId","format":"uuid","source":"upload_sessions"},"downloadGrant":{"field":"download_session_id","schema":"DownloadResolveResponse.data.download_session_id","format":"uuid","source":"download_sessions"},"user":{"field":"id","schema":"PublicUser.id","format":"uuid","source":"auth.users.id"},"team":{"field":"id","schema":"PublicTeam.id","format":"uuid","source":"public_api_teams.id"},"dependency":{"field":"id","schema":"PublicModDependency.id","format":"uuid","source":"public_api_resource_ids"},"job":{"field":"id","schema":"PublicJob.id","format":"uuid","source":"public_api_upload_scan_jobs.id"},"webhookSubscription":{"field":"id","schema":"WebhookSubscription.id","format":"uuid","source":"public_api_webhook_subscriptions.id"},"webhookDelivery":{"field":"delivery_id","schema":"WebhookDelivery.delivery_id","format":"uuid","source":"public_api_webhook_deliveries.id"}},"aliasRules":{"slug":"Slug and localized slugs may be accepted for human lookup, but clients must persist the returned opaque id.","filename":"Filename and original_filename are presentation or download hints and may change without changing logical file identity.","hash":"Hash values verify bytes and support dedupe/integrity checks, but clients address files through artifact ids and download grants."},"idRetentionRules":{"renameOrTitleEdit":"Renaming a mod, changing title/summary/description, or updating presentation fields keeps mod id and version ids.","metadataRebuild":"Rebuilding search/localized metadata keeps existing public ids unless the underlying resource lifecycle creates a new version, artifact, blob, or session.","slugChange":"Changing canonical or localized slugs keeps mod id, version ids, artifact ids, and existing download-grant retry identity.","fileByteReplacement":"Replacing bytes mints a new physical blob and usually a new logical artifact/version binding; metadata-only edits do not require clients to re-upload bytes."},"lifetime":"Every UUID is retained in public_api_identity_ledger for the lifetime of the service. Source bindings and UUIDs are immutable and hard-delete is forbidden.","lookupSemantics":{"unknown":404,"activeOrArchived":200,"tombstoned":410},"nonReuse":"A tombstoned UUID and its entity/source binding can never be registered again; replacement resources always mint a new UUID."},"ownershipModel":{"systemOwned":["Game","Blob","anonymous DownloadGrant"],"userOwned":["User","UploadSession","authenticated DownloadGrant","WebhookSubscription"],"teamOwned":["Mod","ModVersion","Artifact","Dependency"],"inherited":{"Team":"one owner User plus bounded membership roles","Job":"initiating User/resource; worker has execution custody only","WebhookDelivery":"WebhookSubscription owner"},"source":"public_api_domain_entity_contracts.owner_rule and public_api_identity_ledger.owner_kind/owner_id"},"lifecycleModel":{"source":"public_api_domain_entity_contracts + public_api_domain_lifecycle_transitions","failClosed":"Only explicitly listed state edges are allowed; tombstoned has no outgoing edge.","availability":{"states":["active","archived","tombstoned"],"restore":"archived -> active with optimistic lifecycle_version","permanentDelete":"active|archived -> tombstoned; requires a reason and returns 410"},"version":{"states":["draft","validating","scanning","ready","published","archived","withdrawn","failed"],"forward":["draft -> validating|failed|archived","validating -> scanning|failed|archived","scanning -> ready|failed|archived","ready -> published|failed|archived","published -> archived|withdrawn","failed -> archived"],"restore":["archived -> published","withdrawn -> published"],"legacyAliases":{"uploaded":"validating","quarantined":"scanning","approved":"ready","rejected":"failed"}}},"publicationPolicy":{"firstPublish":"Capture a canonical JSON snapshot and SHA-256 over version string, file kind, release channel, game/loader bindings, Blob UUID, size, and content type.","immutableAfterPublish":["version","file_kind","release_channel","game_version_key","loader_key","blob_id","size_bytes","content_type"],"mutableAfterPublish":["presentation-only labels and changelog additions that do not alter the captured snapshot"],"restoreRule":"Archived or withdrawn versions may return to published only when the current canonical snapshot exactly matches the first publication snapshot and the current upload trust gates still pass.","concurrency":"public_api_lifecycle_version is an optimistic lock; stale updates return lifecycle_version_conflict.","compatibility":"Legacy publish-state aliases and existing response fields remain accepted; the canonical lifecycle and snapshot fields are additive."},"canonicalViews":["games","mods","builds","mod_versions","mod_artifacts","mod_dependencies","mod_permissions","mod_licenses","file_lifecycle"],"resourceMapping":{"forum_games":"Game","forum_topics + forum_mod_listings":"Mod","forum_mod_files":["ModVersion","Artifact"],"forum_mod_dependencies":"Dependency","forum_mod_permissions":["Permissions","License"],"forum_mod_language_surfaces":"Localized surfaces","forum_build_*":"Build/Modpack","storage_blobs + upload_sessions + download_sessions":"File lifecycle"},"versionDebt":"Version data is currently split across forum_mod_listings.current_version, forum_mod_files.version, forum_mod_changelog, and build metadata.","versionModel":{"semanticVersionField":"semantic_version","rawVersionField":"version_string","releaseChannelField":"release_channel","changelogSource":"forum_mod_changelog","gameVersionsField":"game_versions","loaderPlatformField":"loader_platforms","immutableArtifactBinding":"mod_versions.primary_artifact_id -> mod_artifacts.id; legacy rows without storage_blobs.blob_id are flagged as legacy_external_url","publicVersionField":"version","publicLoaderField":"loaders","publicArtifactIdsField":"artifact_ids","expandedArtifactDetailsField":"artifacts"},"licenseModel":{"spdxExpressionField":"spdx_expression","customLicenseFallback":"custom_license","assetUsagePermissions":"asset_usage_permissions","sourceUrls":["homepage_url","source_url","original_mod_url"]},"visibilityAccess":{"visibilityValues":["public","unlisted","private","test_mode"],"accessTierValues":["public","restricted","paid"],"entitlements":"Paid/restricted access stays reserved until entitlement-backed files are enabled."},"permissionRoles":{"owner":["read","update_metadata","manage_versions","archive"],"moderator":["read","hide","approve","reject","resolve_reports"],"admin":["read","update_metadata","manage_versions","manage_permissions","manage_visibility","archive","delete"]},"dualReadPlan":"Public API reads canonical views first while old web pages keep reading forum tables. Later write phases can promote views into canonical tables after contract tests and backfill validation.","privacyExportMapping":"Authored topic exports include canonical public_api_resource_ids, release/license fields, and upload session lifecycle rows without staging keys or signed URL material.","catalogReads":{"publicOnlyView":"public_api_catalog_mods filters visibility=public, published topics, non-hidden/non-deleted topics, and non-test-mode listings.","searchEndpointUsesSameFilterSet":true,"searchEndpointSortDefault":"relevance","rpcSortArgument":"p_sort","filters":["game_slug","q","sort","game_version","loader","category"],"sorts":["relevance","latest","trending","downloads"],"localizedFields":["title","summary","description","localized_slugs","categories.label","facets.label"],"cache":"Read responses emit weak ETag and public cache-control with stale-while-revalidate; error responses include X-Request-Id and request_id/trace_id in the envelope."}},"authLifecycle":{"issuer":"https://api.moddingflow.com","firstPartyClients":["website","desktop_mod_manager","embedded_ai_agent","automation_ci_publisher"],"accountControlPlane":{"websiteSurface":"Settings > Developer","publicBearerApi":false,"authentication":"OAuth application management uses the authenticated website account session. These endpoints are an internal account control plane and are intentionally absent from the public bearer API.","internalAccountApi":["GET /api/account/developer/applications","POST /api/account/developer/applications","PATCH /api/account/developer/applications/{application_id}","DELETE /api/account/developer/applications/{application_id}","POST /api/account/developer/applications/{application_id}/scopes"],"consentSurface":"/oauth/consent","sensitiveMutationsRequireAal2":["update_application","request_review_scopes","disable_application"]},"clientRegistry":{"stores":["oauth_clients","oauth_client_redirect_uris","oauth_client_scope_reviews","oauth_user_consents","oauth_access_token_revocations"],"pkceRequiredForPublicClients":true,"confidentialClientsStoreOnlySecretHashes":true,"clientIdContract":{"opaque":true,"extensible":true,"firstPartyFormat":"stable lowercase registry key","selfServiceFormat":"app_<lowercase UUID/hex without a secret>","tolerantClientRule":"Clients must compare client_id only for equality and must not branch on a closed list or parse ownership from it."},"selfServiceRegistration":{"developerSurface":"Settings > Developer","accountApi":"/api/account/developer/applications","defaultClientType":"public","defaultTokenEndpointAuthMethod":"none","contactRequired":true,"maximumRedirectUris":10,"basicScopesRequireManualModeration":false,"basicScopes":["openid","mods:read","files:download","install_plans:resolve","profile:read"],"reviewScopes":["mods:write","versions:write","versions:publish","versions:archive","files:upload","files:manage","teams:admin","webhooks:read","webhooks:write","private_data:read"],"internalOnlyScopes":["agent:*","admin:*"],"sensitiveScopeRequestRequiresAal2":true,"unknownScopesFailClosed":true,"redirectUriPolicy":"Self-service public clients may register at most ten exact HTTPS, claimed custom-scheme, or loopback redirect URIs before OAuth authorization; wildcard and fragment-bearing URIs are rejected."}},"consent":{"version":2,"requiredForSelfServiceApplications":true,"requiredForGrants":["authorization_code","urn:ietf:params:oauth:grant-type:device_code"],"screen":"/oauth/consent","shows":["application identity and verified contact","exact requested scopes","review status for sensitive scopes","redirect destination or device identity"],"resourcePolicy":"all_eligible_resources","resourceDisclosure":"The OAuth grant covers every resource the user is eligible to access within the consented scopes. Resource-restricted automation uses PAT resource grants.","decision":"The signed-in resource owner must explicitly allow or deny the exact requested scope set. Denial returns access_denied and creates no reusable grant.","scopeEscalation":"A previously granted app must ask for consent again when it requests any additional scope."},"authorizationCodePkce":{"primaryFor":["desktop","mobile","public applications"],"responseType":"code","pkceMethod":"S256","authorizationCodeTtlSeconds":300,"exactRedirectUriRequired":true,"browserStateRequired":true,"userPasswordAcceptedByClient":false},"deviceAuthorization":{"intendedFor":["launcher","CLI","headless","input-constrained clients"],"deviceCodeTtlSeconds":900,"initialPollingIntervalSeconds":5,"slowDownIncrementSeconds":5,"maximumPollingIntervalSeconds":60,"userDecisionSurface":"/oauth/device","terminalOutcomes":["access_denied","expired_token","consumed"],"nonTerminalOutcomes":["authorization_pending","slow_down"],"userDecision":"The signed-in user approves or denies the exact client and scope set through the website consent surface; a device code is consumed at most once."},"tokenLifecycle":{"publicApplicationAccessTokenMaxTtlSeconds":900,"accessTokens":"Public-application Bearer access tokens live for at most fifteen minutes. Service-role/internal secrets are rejected as viewer credentials.","accessTokenRevocation":{"endpoint":"POST /oauth/revoke","effective":"next protected API call","maximumPropagationSeconds":60,"runtimeRule":"Every protected call checks token revocation and current client status; disabling an app therefore invalidates its access without waiting for JWT expiry."},"refreshTokens":"Refresh tokens rotate on every use; reuse of any consumed parent marks reuse and revokes the whole token family before another access token is issued.","tokenRotation":"OAuth refresh tokens rotate on every refresh. PAT rotation is atomic, reveals the replacement once, and keeps the previous token valid only for the fixed ten-minute concurrency overlap.","revocation":"Revoke accepts access tokens, refresh tokens, and personal access tokens where applicable. Refresh-token revoke and reuse detection revoke the complete family.","applicationDisable":{"requiresAal2":true,"revokes":["authorization codes","device grants","access tokens","refresh-token families"],"effective":"next protected API call","maximumPropagationSeconds":60},"introspection":"Trusted internal clients only; never exposed to browser or public desktop clients.","agentGatewayAccessTokens":"Token exchange issues short-lived access tokens with audience agent-gateway, narrow agent scopes, and actor/delegation metadata. These tokens are rejected as full public API user session tokens.","tokenExchange":"Embedded AI agent uses urn:ietf:params:oauth:grant-type:token-exchange with a subject user access token, audience agent-gateway, short TTL, and no refresh token.","secureStorage":"Desktop/mobile clients store refresh tokens in the operating-system credential vault, headless clients use a secret manager, public apps never ship a client secret, and logs, URLs, telemetry, crash reports, and source control never contain raw credentials."},"confidentialClients":{"activeRequiresVerifiedUniqueBinding":true,"plaintextStorage":"encrypted runtime secret manager only","databaseStorage":"SHA-256 verifier plus non-secret version, owner, and timestamps","rotationOverlapSeconds":600,"previousSecretFailsClosedAfterOverlap":true,"sameSecretRebindExtendsOverlap":false,"disableClearsPreviousSecret":true},"patApiKeys":{"storage":"public_api_personal_access_tokens stores only token_hash, display_prefix/fingerprint, scopes, resource-scope mode, rotation, expiry, last-used, and revoke metadata; public_api_key_resource_grants stores only mod/project ids.","intendedFor":["personal scripts","CI","scoped automation"],"creationPolicy":"Each user may create an individual API key through POST /v1/api-keys with explicit scopes, expiry, and mod/project grants capped by their owner, maintainer, publisher, or reviewer role. One-time raw secret display means the raw secret is displayed exactly once.","rotationOverlapSeconds":600,"rotationConcurrencyPolicy":"Rotation creates one replacement atomically. The old PAT remains valid for exactly ten minutes so concurrent workers can switch, then fails closed; a second rotation cannot extend the original overlap.","managementEndpoints":["GET /v1/api-keys","POST /v1/api-keys","GET /v1/api-keys/capabilities","POST /v1/api-keys/{api_key_id}/rotate","DELETE /v1/api-keys/{api_key_id}","GET /v1/mods/{mod_id}/team-members","PUT /v1/mods/{mod_id}/team-members/{user_id}","DELETE /v1/mods/{mod_id}/team-members/{user_id}"],"visibleFields":["id","name","fingerprint","display_prefix","scopes","resources","resource_scope_mode","last_used_at","expires_at","revoked_at","rotation_generation"],"auditEvents":["personal_access_token_created","personal_access_token_used","personal_access_token_rotated","personal_access_token_revoked"],"secretLoggingPolicy":"Audit and write events record api_key_id and safe fingerprint only; raw secrets and token_hash values never enter request metadata or logs."},"rateLimits":{"source":"public_api_rate_limit_policies","dimensions":["client_id","actor_user_id","token_id_or_api_key_id","scope","token_kind"],"bucketKeys":["application/client","user","token"],"timing":"Evaluate before storage, signing, upload, download, or agent side effects."},"auditTrail":{"source":"public_api_auth_audit_events","appendOnly":true,"eventFamilies":["application registration and disable","scope review request and decision","consent allow and deny","device authorization approve and deny","token issuance","refresh rotation","refresh reuse detection and family revoke","revoke","introspection","denied scopes","PAT usage","rate-limit hits","agent token exchange"]}},"publishPipeline":{"forumTablesRemainPrimaryWriteModel":true,"uploadExecutor":"External upload routes reuse the existing R2 storage upload executor; invalid archives are rejected before final bucket promotion.","validation":["size_bytes","sha256","mime_magic_bytes","zip_path_traversal","zip_compression_ratio","av_scan_adapter"],"auditTable":"public_api_write_audit_events","idempotencyTable":"public_api_write_idempotency_keys","scanWorkerQueue":"public_api_upload_scan_jobs records queued, processing, succeeded, rejected, failed, and cancelled scan worker transitions.","moderationHooks":"public_api_moderation_reviews records owner publish and future moderator review hooks.","webhookQueue":"public_api_webhook_events stores upload and publish events for async delivery."},"agentGateway":{"rustOwnedBoundary":true,"audience":"agent-gateway","schemaVersion":1,"supportedSchemaVersions":[1],"stageStatus":"stabilizing_tool_contracts","tokenExchangeGrant":"urn:ietf:params:oauth:grant-type:token-exchange","subjectTokenType":"urn:ietf:params:oauth:token-type:access_token","fullUserSessionTokenAllowed":false,"tools":["mods.search","mods.get","install_plans.resolve","downloads.resolve","versions.compare","publisher.draft","publisher.validate"],"dryRunByDefault":["publisher.draft","publisher.validate"],"confirmationRequiredFor":["publish","delete/archive","permission changes","paid/restricted access changes"],"policySandbox":{"arbitraryUrlFetch":false,"privateDataExfiltration":false,"downloadActions":"allowlisted public artifacts only","redactionInLogs":true},"mcpFacade":"deferred until tool contracts stabilize","auditLog":"public_api_agent_audit_events","decisionRecords":"public_api_agent_decision_records"},"operations":{"metricsSink":"observability_metric_samples","routeMetrics":["public_api.requests.total","public_api.request.latency_ms","public_api.responses.4xx.total","public_api.responses.5xx.total","public_api.rate_limit_hits.total","public_api.failure_reasons.total"],"perClientDimensions":["client_key","flow","operation","status_class","token_kind"],"otel":{"traceContextHeader":"traceparent","spanBoundary":"public API route handlers, Mod Manager download/install-plan calls, OAuth token lifecycle, and Agent Gateway tool calls"},"dashboards":{"source":"public_api_observability_dashboard","flows":["auth","catalog","uploads","downloads","install_plans","agent"]},"alerts":{"source":"public_api_operational_alert_policies","criticalFlows":["Rust API unavailable","DB pool saturation","R2 signing failures","Bunny fallback spike","AV scanner backlog","token abuse/rate-limit spike"]},"abuseControls":{"requestDimensions":["per-IP","per-user","per-client","per-token"],"byteBudgets":["upload size budget","download egress budget"],"costBudgetRpc":"consume_public_api_abuse_budget","timing":"Request and byte budgets are checked before storage, signing, upload, download, or agent side effects."},"backupDr":{"readinessReport":"public_api_operational_readiness_report","operatorGates":["postgresql_pitr","r2_object_versioning","restore_drill","runtime_secret_manager"],"verifiedArtifacts":["openapi/moddingflow-public-api.v1.json"]}}},"paths":{"/games":{"get":{"tags":["Catalog"],"operationId":"listGames","summary":"List supported games.","description":"Returns enabled games from the stable public catalog read model.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"Enabled public catalog games.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","title":{"en":"example-value"},"icon_path":"example-value","is_enabled":true,"sort_order":1,"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}]}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listGames","parameters":{"path":{},"query":{},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","title":{"en":"example-value"},"icon_path":"example-value","is_enabled":true,"sort_order":1,"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}]}}}}}},"/games/{game_slug}":{"get":{"tags":["Catalog"],"operationId":"getGame","summary":"Get a supported game by slug.","description":"Returns one enabled public catalog game by stable slug.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/GamePathSlug"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The requested public catalog game.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","title":{"en":"example-value"},"icon_path":"example-value","is_enabled":true,"sort_order":1,"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getGame","parameters":{"path":{"game_slug":"example-value"},"query":{},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","title":{"en":"example-value"},"icon_path":"example-value","is_enabled":true,"sort_order":1,"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}}}}}},"/mods":{"get":{"tags":["Catalog"],"operationId":"listMods","summary":"List public mods.","description":"Cursor-paginated read-only catalog listing with game, search, sort, category, loader, and game-version filters. The contract exposes stable public resource ids and localized text fields without leaking forum table names.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/GameSlug"},{"$ref":"#/components/parameters/SearchQuery"},{"$ref":"#/components/parameters/Sort"},{"$ref":"#/components/parameters/GameVersion"},{"$ref":"#/components/parameters/Loader"},{"$ref":"#/components/parameters/Category"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"A stable cursor page of public mods.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"id":"22222222-2222-4222-8222-222222222222","game_id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","localized_slugs":{},"game_slug":"example-value","title":{"en":"example-value"},"summary":{"en":"example-value"},"description":{"en":"example-value"},"latest_version":"example-value","visibility":"public","access_tier":"public","is_nsfw":true,"updated_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listMods","parameters":{"path":{},"query":{"cursor":"v1.example-opaque-cursor","limit":25,"game_slug":"example-value","q":"sky ui","sort":"relevance","game_version":"1.6.1170","loader":"example-value","category":"example-value"},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"id":"22222222-2222-4222-8222-222222222222","game_id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","localized_slugs":{},"game_slug":"example-value","title":{"en":"example-value"},"summary":{"en":"example-value"},"description":{"en":"example-value"},"latest_version":"example-value","visibility":"public","access_tier":"public","is_nsfw":true,"updated_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}},"post":{"tags":["Publishing"],"operationId":"createModDraft","summary":"Create a mod draft.","description":"Creates a creator-owned mod shell on the existing forum-backed write model and assigns a stable public mod UUID.","security":[{"OAuth2Pkce":["mods:write"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModRequest"},"examples":{"representative":{"summary":"Representative request","value":{"game_slug":"example-value","language_code":"en","slug":"example-value","title":"Representative Moddingflow example","summary":"example-value","description_md":"Representative value for the public API contract.","category_slugs":["example-value"],"current_version":"example-value","homepage_url":"https://example.invalid/moddingflow-resource","source_url":"https://example.invalid/moddingflow-resource","is_nsfw":true}}}}}},"responses":{"201":{"description":"Mod draft created.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiModWriteResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"status":1,"data":{"id":"22222222-2222-4222-8222-222222222222","source_topic_id":1,"state":"draft","slug":"example-value"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"createModDraft","parameters":{"path":{},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"game_slug":"example-value","language_code":"en","slug":"example-value","title":"Representative Moddingflow example","summary":"example-value","description_md":"Representative value for the public API contract.","category_slugs":["example-value"],"current_version":"example-value","homepage_url":"https://example.invalid/moddingflow-resource","source_url":"https://example.invalid/moddingflow-resource","is_nsfw":true}},"success":{"status":"201","mediaType":"application/json","value":{"ok":true,"status":1,"data":{"id":"22222222-2222-4222-8222-222222222222","source_topic_id":1,"state":"draft","slug":"example-value"}}}}}},"/mods/{mod_id}":{"get":{"tags":["Catalog"],"operationId":"getMod","summary":"Get a public mod by stable id or slug.","description":"Returns a single public mod resource. The path accepts the stable UUID or the canonical slug; clients should persist UUID ids because slugs remain display aliases.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/ModIdOrSlug"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The requested public mod.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"id":"22222222-2222-4222-8222-222222222222","game_id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","localized_slugs":{},"game_slug":"example-value","title":{"en":"example-value"},"summary":{"en":"example-value"},"description":{"en":"example-value"},"latest_version":"example-value","visibility":"public","access_tier":"public","is_nsfw":true,"updated_at":"2026-07-11T10:30:00Z"}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getMod","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222"},"query":{},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"id":"22222222-2222-4222-8222-222222222222","game_id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","localized_slugs":{},"game_slug":"example-value","title":{"en":"example-value"},"summary":{"en":"example-value"},"description":{"en":"example-value"},"latest_version":"example-value","visibility":"public","access_tier":"public","is_nsfw":true,"updated_at":"2026-07-11T10:30:00Z"}}}}},"patch":{"tags":["Publishing"],"operationId":"updateModDraft","summary":"Update a mod draft or owned mod metadata.","description":"Updates owner-editable mod metadata through the public API while keeping internal forum ids out of the external contract.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["412","428"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_problem_details_headers_and_numeric_status","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:write"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/IfMatch"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModRequest"},"examples":{"representative":{"summary":"Representative request","value":{"game_slug":"example-value","language_code":"en","slug":"example-value","title":"Representative Moddingflow example","summary":"example-value","description_md":"Representative value for the public API contract.","category_slugs":["example-value"],"current_version":"example-value","homepage_url":"https://example.invalid/moddingflow-resource","source_url":"https://example.invalid/moddingflow-resource","is_nsfw":true}}}}}},"responses":{"200":{"description":"Mod metadata updated.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiModWriteResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"status":1,"data":{"id":"22222222-2222-4222-8222-222222222222","source_topic_id":1,"state":"draft","slug":"example-value"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/ValidationFailed"},"428":{"$ref":"#/components/responses/PreconditionRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"updateModDraft","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","If-Match":"\"example-etag\"","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"game_slug":"example-value","language_code":"en","slug":"example-value","title":"Representative Moddingflow example","summary":"example-value","description_md":"Representative value for the public API contract.","category_slugs":["example-value"],"current_version":"example-value","homepage_url":"https://example.invalid/moddingflow-resource","source_url":"https://example.invalid/moddingflow-resource","is_nsfw":true}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"status":1,"data":{"id":"22222222-2222-4222-8222-222222222222","source_topic_id":1,"state":"draft","slug":"example-value"}}}}}},"/mods/{mod_id}/versions":{"get":{"tags":["Catalog"],"operationId":"listModVersions","summary":"List public versions for a mod.","description":"Returns public version metadata, stable artifact ids, and optional complete provider-opaque artifact details when current managed integrity, scan, and resolve evidence exists.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"A stable cursor page of public mod versions.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModVersionListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","primary_artifact_id":"44444444-4444-4444-8444-444444444444","version":"1.6.1170","semantic_version":"example-value","release_channel":"stable","game_versions":["1.6.1170"],"loaders":["example-value"],"artifact_ids":["44444444-4444-4444-8444-444444444444"],"artifacts":[{"id":"44444444-4444-4444-8444-444444444444","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","file_kind":"main","label":"Main archive","original_filename":"mod-1.0.0.zip","content_type":"application/zip","game_version_key":"1.6.1170","loader_key":null,"size_bytes":1048576,"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"artifact_source":"r2_blob","status":"published","scan_status":"clean","download_metadata":{"resolve_endpoint":"/v1/downloads/44444444-4444-4444-8444-444444444444/resolve","range_supported":true}}],"has_immutable_artifact_binding":true,"changelog":[{"version":"1.6.1170","body_md":"example-value","released_at":"2026-07-11T10:30:00Z"}],"published_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listModVersions","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222"},"query":{"cursor":"v1.example-opaque-cursor","limit":25},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","primary_artifact_id":"44444444-4444-4444-8444-444444444444","version":"1.6.1170","semantic_version":"example-value","release_channel":"stable","game_versions":["1.6.1170"],"loaders":["example-value"],"artifact_ids":["44444444-4444-4444-8444-444444444444"],"artifacts":[{"id":"44444444-4444-4444-8444-444444444444","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","file_kind":"main","label":"Main archive","original_filename":"mod-1.0.0.zip","content_type":"application/zip","game_version_key":"1.6.1170","loader_key":null,"size_bytes":1048576,"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"artifact_source":"r2_blob","status":"published","scan_status":"clean","download_metadata":{"resolve_endpoint":"/v1/downloads/44444444-4444-4444-8444-444444444444/resolve","range_supported":true}}],"has_immutable_artifact_binding":true,"changelog":[{"version":"1.6.1170","body_md":"example-value","released_at":"2026-07-11T10:30:00Z"}],"published_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}},"post":{"tags":["Publishing"],"operationId":"createModVersion","summary":"Create a mod version draft.","description":"Creates a version row, stable version/artifact UUIDs, and the upload file id required by POST /uploads.","security":[{"OAuth2Pkce":["versions:write"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModVersionRequest"},"examples":{"representative":{"summary":"Representative request","value":{"version":"1.6.1170","label":"example-value","file_kind":"main","file_category":"main","release_channel":"stable","game_version_key":"1.6.1170","loader_key":"example-value","changelog":"example-value","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}]}}}}}},"responses":{"201":{"description":"Version draft created.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiVersionWriteResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"createModVersion","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"version":"1.6.1170","label":"example-value","file_kind":"main","file_category":"main","release_channel":"stable","game_version_key":"1.6.1170","loader_key":"example-value","changelog":"example-value","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}]}},"success":{"status":"201","mediaType":"application/json","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}},"/mods/{mod_id}/versions/{version_id}":{"get":{"tags":["Catalog"],"operationId":"getModVersion","summary":"Get one public version for a mod.","description":"Returns one public version metadata resource, stable artifact ids, and optional complete provider-opaque artifact details without exposing storage object keys.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/ModVersionId"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"The requested public mod version.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModVersionResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","primary_artifact_id":"44444444-4444-4444-8444-444444444444","version":"1.6.1170","semantic_version":"example-value","release_channel":"stable","game_versions":["1.6.1170"],"loaders":["example-value"],"artifact_ids":["44444444-4444-4444-8444-444444444444"],"artifacts":[{"id":"44444444-4444-4444-8444-444444444444","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","file_kind":"main","label":"Main archive","original_filename":"mod-1.0.0.zip","content_type":"application/zip","game_version_key":"1.6.1170","loader_key":null,"size_bytes":1048576,"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"artifact_source":"r2_blob","status":"published","scan_status":"clean","download_metadata":{"resolve_endpoint":"/v1/downloads/44444444-4444-4444-8444-444444444444/resolve","range_supported":true}}],"has_immutable_artifact_binding":true,"changelog":[{"version":"1.6.1170","body_md":"example-value","released_at":"2026-07-11T10:30:00Z"}],"published_at":"2026-07-11T10:30:00Z"}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getModVersion","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333"},"query":{},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","primary_artifact_id":"44444444-4444-4444-8444-444444444444","version":"1.6.1170","semantic_version":"example-value","release_channel":"stable","game_versions":["1.6.1170"],"loaders":["example-value"],"artifact_ids":["44444444-4444-4444-8444-444444444444"],"artifacts":[{"id":"44444444-4444-4444-8444-444444444444","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","file_kind":"main","label":"Main archive","original_filename":"mod-1.0.0.zip","content_type":"application/zip","game_version_key":"1.6.1170","loader_key":null,"size_bytes":1048576,"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"artifact_source":"r2_blob","status":"published","scan_status":"clean","download_metadata":{"resolve_endpoint":"/v1/downloads/44444444-4444-4444-8444-444444444444/resolve","range_supported":true}}],"has_immutable_artifact_binding":true,"changelog":[{"version":"1.6.1170","body_md":"example-value","released_at":"2026-07-11T10:30:00Z"}],"published_at":"2026-07-11T10:30:00Z"}}}}},"patch":{"tags":["Publishing"],"operationId":"updateModVersion","summary":"Update a mod version draft.","description":"Updates owner-editable version metadata before publish or archival.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["412","428"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_problem_details_headers_and_numeric_status","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["versions:write"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/ModVersionId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/IfMatch"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModVersionRequest"},"examples":{"representative":{"summary":"Representative request","value":{"version":"1.6.1170","label":"example-value","file_kind":"main","file_category":"main","release_channel":"stable","game_version_key":"1.6.1170","loader_key":"example-value","changelog":"example-value","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}]}}}}}},"responses":{"200":{"description":"Version metadata updated.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiVersionWriteResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/ValidationFailed"},"428":{"$ref":"#/components/responses/PreconditionRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"updateModVersion","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","If-Match":"\"example-etag\"","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"version":"1.6.1170","label":"example-value","file_kind":"main","file_category":"main","release_channel":"stable","game_version_key":"1.6.1170","loader_key":"example-value","changelog":"example-value","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}]}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}},"/mods/{mod_id}/dependencies":{"get":{"tags":["Catalog"],"operationId":"listModDependencies","summary":"List public dependencies for a mod.","description":"Returns required mods, external requirements, and incompatibility metadata for public install planning.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"Public dependencies for the requested mod.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModDependencyListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","mod_id":"22222222-2222-4222-8222-222222222222","target_mod_id":"22222222-2222-4222-8222-222222222222","dependency_kind":"required_mod","dependency_semantic":"required","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value","relation":"example-value","sort_order":1,"created_at":"2026-07-11T10:30:00Z"}]}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listModDependencies","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222"},"query":{},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","mod_id":"22222222-2222-4222-8222-222222222222","target_mod_id":"22222222-2222-4222-8222-222222222222","dependency_kind":"required_mod","dependency_semantic":"required","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value","relation":"example-value","sort_order":1,"created_at":"2026-07-11T10:30:00Z"}]}}}}}},"/categories":{"get":{"tags":["Catalog"],"operationId":"listCatalogCategories","summary":"List catalog categories and facets.","description":"Returns category, loader, edition, and game-version facet values with localized labels and public mod counts.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/GameSlug"},{"$ref":"#/components/parameters/Language"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"Catalog categories and facet values.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogFacetListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"facet":"example-value","value":"example-value","label":{"en":"example-value"},"game_id":"550e8400-e29b-41d4-a716-446655440000","game_slug":"example-value","count":0}],"label_language":"example-value"}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listCatalogCategories","parameters":{"path":{},"query":{"game_slug":"example-value","lang":"en"},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"facet":"example-value","value":"example-value","label":{"en":"example-value"},"game_id":"550e8400-e29b-41d4-a716-446655440000","game_slug":"example-value","count":0}],"label_language":"example-value"}}}}}},"/search":{"get":{"tags":["Catalog"],"operationId":"searchCatalog","summary":"Search the public catalog.","description":"Runs read-only public catalog search with the same filters, sort values, and pagination envelope as /mods, ordered by relevance by default.","x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["304"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_empty_payload_headers_and_numeric_status_as_conditional_control_signal","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts"},"security":[{"OAuth2Pkce":["mods:read"]},{"BearerAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/RequiredSearchQuery"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/GameSlug"},{"$ref":"#/components/parameters/Sort"},{"$ref":"#/components/parameters/GameVersion"},{"$ref":"#/components/parameters/Loader"},{"$ref":"#/components/parameters/Category"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"A relevance-ordered cursor page of public mods.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogSearchResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"id":"22222222-2222-4222-8222-222222222222","game_id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","localized_slugs":{},"game_slug":"example-value","title":{"en":"example-value"},"summary":{"en":"example-value"},"description":{"en":"example-value"},"latest_version":"example-value","visibility":"public","access_tier":"public","is_nsfw":true,"updated_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}}}},"304":{"$ref":"#/components/responses/CatalogNotModified"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"searchCatalog","parameters":{"path":{},"query":{"q":"sky ui","cursor":"v1.example-opaque-cursor","limit":25,"game_slug":"example-value","sort":"relevance","game_version":"1.6.1170","loader":"example-value","category":"example-value"},"header":{"If-None-Match":"W/\"example-etag\""}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"id":"22222222-2222-4222-8222-222222222222","game_id":"550e8400-e29b-41d4-a716-446655440000","slug":"example-value","localized_slugs":{},"game_slug":"example-value","title":{"en":"example-value"},"summary":{"en":"example-value"},"description":{"en":"example-value"},"latest_version":"example-value","visibility":"public","access_tier":"public","is_nsfw":true,"updated_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}}},"/uploads":{"post":{"tags":["Uploads"],"operationId":"createUploadSession","summary":"Create an upload session.","description":"Creates an external upload session through the existing R2 upload executor. The response returns either a single signed PUT URL or multipart helper endpoints. A single PUT client must send every returned header, including the signed case-insensitive If-None-Match: * precondition. A conditional 412 is an ambiguous existing write: do not overwrite or start a second transfer; call the returned completion endpoint so server-side HEAD, immutable sealing, and SHA-256 verification determine the outcome. Every write requires Idempotency-Key.","security":[{"OAuth2Pkce":["files:upload"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadRequest"},"examples":{"representative":{"summary":"Representative request","value":{"fileId":1,"originalFilename":"Example Mod","sizeBytes":1,"contentType":"example-value","expectedHashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sha1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}}}}}}},"responses":{"201":{"description":"Upload session created.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"},"X-Upload-Correlation-Id":{"$ref":"#/components/headers/XUploadCorrelationId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"status":201,"data":{"sessionId":"550e8400-e29b-41d4-a716-446655440000","supportCorrelationId":"upl_aaaaaaaaaaaaaaaaaaaa","strategy":"single","fileId":1,"expiresIn":1,"expiresAt":"2026-07-11T10:30:00Z","status":"created","method":"PUT","uploadUrl":"https://example.invalid/moddingflow-resource","urlExpiresIn":1,"headers":{"if-none-match":"*"},"uploadId":"550e8400-e29b-41d4-a716-446655440000"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"createUploadSession","parameters":{"path":{},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"fileId":1,"originalFilename":"Example Mod","sizeBytes":1,"contentType":"example-value","expectedHashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sha1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}}},"success":{"status":"201","mediaType":"application/json","value":{"ok":true,"status":201,"data":{"sessionId":"550e8400-e29b-41d4-a716-446655440000","supportCorrelationId":"upl_aaaaaaaaaaaaaaaaaaaa","strategy":"single","fileId":1,"expiresIn":1,"expiresAt":"2026-07-11T10:30:00Z","status":"created","method":"PUT","uploadUrl":"https://example.invalid/moddingflow-resource","urlExpiresIn":1,"headers":{"if-none-match":"*"},"uploadId":"550e8400-e29b-41d4-a716-446655440000"}}}}}},"/uploads:validate":{"post":{"tags":["Uploads"],"operationId":"validateUploadRequest","summary":"Validate an upload request without creating a session.","description":"Runs authentication, ownership, archive-format, size-limit and checksum-policy preflight checks for CI. It does not create an upload session, consume the upload byte budget, sign storage URLs, upload bytes, or publish a version.","x-moddingflow-dry-run":{"sideEffectFreeResources":true,"createsUploadSession":false,"uploadsBytes":false,"publishesVersion":false},"security":[{"OAuth2Pkce":["files:upload"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateUploadRequest"},"examples":{"representative":{"summary":"Representative request","value":{"fileId":1,"originalFilename":"Example Mod","sizeBytes":1,"contentType":"example-value","expectedHashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sha1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}}}}}}},"responses":{"200":{"description":"Upload request is valid and no resource was created.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadValidationResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"valid":true,"mode":"validate_only","fileId":1,"originalFilename":"Example Mod","sizeBytes":1,"contentType":"example-value","expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","metadata":{"archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}},"strategy":{"kind":"single","partSizeBytes":1,"parallelism":1},"constraints":{"maxArchiveBytes":1,"sha256Required":true},"checks":["authorization","authorization","authorization","authorization","authorization"]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"validateUploadRequest","parameters":{"path":{},"query":{},"header":{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"fileId":1,"originalFilename":"Example Mod","sizeBytes":1,"contentType":"example-value","expectedHashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sha1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"valid":true,"mode":"validate_only","fileId":1,"originalFilename":"Example Mod","sizeBytes":1,"contentType":"example-value","expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","metadata":{"archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}},"strategy":{"kind":"single","partSizeBytes":1,"parallelism":1},"constraints":{"maxArchiveBytes":1,"sha256Required":true},"checks":["authorization","authorization","authorization","authorization","authorization"]}}}}}},"/uploads/{upload_id}":{"get":{"tags":["Uploads"],"operationId":"getUploadSession","summary":"Get upload session status.","description":"Returns owner-scoped upload-session lifecycle status, expiry, retry/resume hints, and stable action endpoints without exposing object storage keys or signed URL secrets.","security":[{"OAuth2Pkce":["files:upload"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/UploadId"},{"$ref":"#/components/parameters/Traceparent"}],"responses":{"200":{"description":"Upload session lifecycle status.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"},"X-Upload-Correlation-Id":{"$ref":"#/components/headers/XUploadCorrelationId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionStatusResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"sessionId":"550e8400-e29b-41d4-a716-446655440000","supportCorrelationId":"upl_aaaaaaaaaaaaaaaaaaaa","status":"created","lifecycleStatus":"draft","strategy":"single","fileId":1,"expectedSizeBytes":1,"actualSizeBytes":1,"expiresAt":"2026-07-11T10:30:00Z","expiresIn":0,"job":{"id":"550e8400-e29b-41d4-a716-446655440000","kind":"upload_processing","resource":{"kind":"upload-session","id":"550e8400-e29b-41d4-a716-446655440000"},"status":"queued","terminal":true,"statusEndpoint":"example-value","reason":"example-value","progress":{"completed":0,"total":1,"percent":0,"unit":"job"},"retry":{"retryable":true,"strategy":"worker_retry","retryAfterSeconds":0,"attempt":0,"maxAttempts":1,"nextAttemptAt":"2026-07-11T10:30:00Z"},"createdAt":"2026-07-11T10:30:00Z","updatedAt":"2026-07-11T10:30:00Z","startedAt":"2026-07-11T10:30:00Z"},"metadata":{"archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}},"reason":"example-value","endpoints":{"status":"example-value","parts":"example-value","complete":"example-value","abort":"example-value"},"retry":{"idempotencyKeyRequired":true,"refreshSignedUrls":"example-value","resumeStrategy":"example-value","metadataOnlyChangesRequireReupload":false},"resume":{"supported":true,"strategy":"single","partSizeBytes":1,"parallelism":1},"progress":{"available":true,"uploadedBytes":0,"totalBytes":1,"percent":0,"totalParts":1,"completedParts":[{"partNumber":1,"etag":"W/\"example-etag\"","sizeBytes":0}]}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getUploadSession","parameters":{"path":{"upload_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"sessionId":"550e8400-e29b-41d4-a716-446655440000","supportCorrelationId":"upl_aaaaaaaaaaaaaaaaaaaa","status":"created","lifecycleStatus":"draft","strategy":"single","fileId":1,"expectedSizeBytes":1,"actualSizeBytes":1,"expiresAt":"2026-07-11T10:30:00Z","expiresIn":0,"job":{"id":"550e8400-e29b-41d4-a716-446655440000","kind":"upload_processing","resource":{"kind":"upload-session","id":"550e8400-e29b-41d4-a716-446655440000"},"status":"queued","terminal":true,"statusEndpoint":"example-value","reason":"example-value","progress":{"completed":0,"total":1,"percent":0,"unit":"job"},"retry":{"retryable":true,"strategy":"worker_retry","retryAfterSeconds":0,"attempt":0,"maxAttempts":1,"nextAttemptAt":"2026-07-11T10:30:00Z"},"createdAt":"2026-07-11T10:30:00Z","updatedAt":"2026-07-11T10:30:00Z","startedAt":"2026-07-11T10:30:00Z"},"metadata":{"archiveManifest":{"format":"zip","entryCount":0,"totalUncompressedBytes":0,"rootPaths":["example-value"],"manifestSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"buildMetadata":{"source":"local","commitSha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflowRunId":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"}},"reason":"example-value","endpoints":{"status":"example-value","parts":"example-value","complete":"example-value","abort":"example-value"},"retry":{"idempotencyKeyRequired":true,"refreshSignedUrls":"example-value","resumeStrategy":"example-value","metadataOnlyChangesRequireReupload":false},"resume":{"supported":true,"strategy":"single","partSizeBytes":1,"parallelism":1},"progress":{"available":true,"uploadedBytes":0,"totalBytes":1,"percent":0,"totalParts":1,"completedParts":[{"partNumber":1,"etag":"W/\"example-etag\"","sizeBytes":0}]}}}}}}},"/downloads/{artifact_id}/resolve":{"post":{"tags":["Downloads"],"operationId":"resolveDownload","summary":"Resolve an artifact download.","description":"Creates or resumes an actor-bound durable download job with one stable grant, then returns operation-scoped GET and HEAD URLs, file integrity metadata, byte-range and conditional-request support, and refresh hints. On 429, clients retain download_job, honor Retry-After, and retry with job_id plus monotonic progress. Private storage object keys and signing secrets are never returned.","x-moddingflow-download-flow":{"createsDownloadGrant":true,"grantIdField":"download_session_id","grantObjectField":"download_grant","grantTtlFields":["expires_at","expires_in","refresh_after_seconds"],"signedUrlFields":["primary_url","head_url","fallback_url"],"storageProviderIndependent":true},"x-moddingflow-durable-download-job":{"durableJobField":"download_job","durableJobRequestField":"job_id","rateLimitIdentityStable":true,"providerEffectsOnRateLimit":0},"security":[{"OAuth2Pkce":["files:download"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/ArtifactId"},{"$ref":"#/components/parameters/OptionalIdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadResolveRequest"},"examples":{"modManagerDownloadResolve":{"summary":"Mod Manager download resolve","value":{"client":"mod_manager","preferred_cdn":"primary","job_id":"55555555-5555-4555-8555-555555555555","progress":{"bytes_received":12582912,"etag":"\"skyui-etag\""}}}}}}},"responses":{"200":{"description":"Resolved download links and verification metadata.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadResolveResponse"},"examples":{"resolvedDownloadWithVerification":{"summary":"Resolved download with verification","value":{"ok":true,"data":{"artifact_id":"33333333-3333-4333-8333-333333333333","download_session_id":"44444444-4444-4444-8444-444444444444","download_job":{"id":"55555555-5555-4555-8555-555555555555","grant_id":"44444444-4444-4444-8444-444444444444","artifact_id":"33333333-3333-4333-8333-333333333333","status":"grant_active","bytes_received":12582912,"etag":"\"skyui-etag\"","attempt_count":2,"rate_limit_count":3,"provider_effect_count":1,"next_attempt_at":null},"primary_url":"https://cdn.example.invalid/mods/skyui.7z?signature=short-lived","head_url":"https://storage.example.invalid/mods/skyui.7z?signature=head-short-lived","expires_at":"2026-07-08T12:00:00Z","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"size_bytes":73400320,"accept_ranges":"bytes","conditional_headers":["If-Match","If-None-Match","If-Modified-Since","If-Unmodified-Since"],"range_supported":true,"resume":{"range_supported":true,"etag":"\"skyui-etag\"","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"head_url":"https://storage.example.invalid/mods/skyui.7z?signature=head-short-lived","conditional_headers":["If-Match","If-None-Match","If-Modified-Since","If-Unmodified-Since"],"chunk_size_hint_bytes":8388608,"signed_url_expiry_refresh":"refresh resolve before expires_at"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"resolveDownload","parameters":{"path":{"artifact_id":"44444444-4444-4444-8444-444444444444"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"client":"mod_manager","preferred_cdn":"primary","job_id":"55555555-5555-4555-8555-555555555555","progress":{"bytes_received":12582912,"etag":"\"skyui-etag\""}}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"artifact_id":"33333333-3333-4333-8333-333333333333","download_session_id":"44444444-4444-4444-8444-444444444444","download_job":{"id":"55555555-5555-4555-8555-555555555555","grant_id":"44444444-4444-4444-8444-444444444444","artifact_id":"33333333-3333-4333-8333-333333333333","status":"grant_active","bytes_received":12582912,"etag":"\"skyui-etag\"","attempt_count":2,"rate_limit_count":3,"provider_effect_count":1,"next_attempt_at":null},"primary_url":"https://cdn.example.invalid/mods/skyui.7z?signature=short-lived","head_url":"https://storage.example.invalid/mods/skyui.7z?signature=head-short-lived","expires_at":"2026-07-08T12:00:00Z","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"size_bytes":73400320,"accept_ranges":"bytes","conditional_headers":["If-Match","If-None-Match","If-Modified-Since","If-Unmodified-Since"],"range_supported":true,"resume":{"range_supported":true,"etag":"\"skyui-etag\"","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"head_url":"https://storage.example.invalid/mods/skyui.7z?signature=head-short-lived","conditional_headers":["If-Match","If-None-Match","If-Modified-Since","If-Unmodified-Since"],"chunk_size_hint_bytes":8388608,"signed_url_expiry_refresh":"refresh resolve before expires_at"}}}}}}},"/downloads/{artifact_id}/fallback":{"post":{"tags":["Downloads"],"operationId":"resolveDownloadFallback","summary":"Resolve a fallback artifact download.","description":"Returns a short-lived Bunny fallback URL for an existing download session after the client reports a primary CDN failure or trusted geo routing requires fallback. The endpoint reuses the same artifact-aware authorization and session binding as download resolve.","x-moddingflow-download-flow":{"requiresDownloadGrant":true,"grantIdRequestField":"downloadSessionId","expiredGrantResponse":410,"storageProviderIndependent":true},"security":[{"OAuth2Pkce":["files:download"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/ArtifactId"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadFallbackRequest"},"examples":{"representative":{"summary":"Representative request","value":{"downloadSessionId":"550e8400-e29b-41d4-a716-446655440000","cloudflareProbeFailed":true,"probe":{"ok":true,"failed":true,"status":1,"bytesReceived":0,"reason":"example-value"}}}}}}},"responses":{"200":{"description":"Resolved fallback download link.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadFallbackResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"artifact_id":"44444444-4444-4444-8444-444444444444","download_session_id":"550e8400-e29b-41d4-a716-446655440000","fallback_url":"https://example.invalid/moddingflow-resource","url":"https://example.invalid/moddingflow-resource","provider":"example-value","reason":"bunny_ru_geo","expires_at":"2026-07-11T10:30:00Z","expires_in":1}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"resolveDownloadFallback","parameters":{"path":{"artifact_id":"44444444-4444-4444-8444-444444444444"},"query":{},"header":{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"downloadSessionId":"550e8400-e29b-41d4-a716-446655440000","cloudflareProbeFailed":true,"probe":{"ok":true,"failed":true,"status":1,"bytesReceived":0,"reason":"example-value"}}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"artifact_id":"44444444-4444-4444-8444-444444444444","download_session_id":"550e8400-e29b-41d4-a716-446655440000","fallback_url":"https://example.invalid/moddingflow-resource","url":"https://example.invalid/moddingflow-resource","provider":"example-value","reason":"bunny_ru_geo","expires_at":"2026-07-11T10:30:00Z","expires_in":1}}}}}},"/install-plans:resolve":{"post":{"tags":["Install Plans"],"operationId":"resolveInstallPlan","summary":"Resolve an install plan.","description":"Creates a deterministic dependency and artifact plan for Mod Manager clients. The operation is idempotency-aware because clients may retry after network interruption.","security":[{"OAuth2Pkce":["install_plans:resolve"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]},{}],"parameters":[{"$ref":"#/components/parameters/OptionalIdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallPlanResolveRequest"},"examples":{"modManagerFirstInstallPlan":{"summary":"Mod Manager first install plan","value":{"game_slug":"skyrim-se","game_version":"1.6.1170","mod_ids":["11111111-1111-4111-8111-111111111111"],"include_optional":false}}}}}},"responses":{"200":{"description":"Resolved install plan.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallPlanResolveResponse"},"examples":{"resolvedInstallPlan":{"summary":"Resolved install plan","value":{"ok":true,"data":{"plan_id":"22222222-2222-4222-8222-222222222222","game_slug":"skyrim-se","game_version":"1.6.1170","platform":"windows","loader":"skse","release_channel":"stable","dependency_constraints":[],"conflicts":[],"install_order":[{"item_id":"44444444-4444-4444-8444-444444444444","mod_id":"11111111-1111-4111-8111-111111111111","version_id":"22222222-2222-4222-8222-222222222222","artifact_id":"33333333-3333-4333-8333-333333333333","required":true,"selection_kind":"selected_artifact","decision_reasons":["selected_by_artifact","release_channel:stable","platform:windows","loader:skse"],"download_resolve_endpoint":"/v1/downloads/33333333-3333-4333-8333-333333333333/resolve","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}],"warnings":[]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"resolveInstallPlan","parameters":{"path":{},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"game_slug":"skyrim-se","game_version":"1.6.1170","mod_ids":["11111111-1111-4111-8111-111111111111"],"include_optional":false}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"plan_id":"22222222-2222-4222-8222-222222222222","game_slug":"skyrim-se","game_version":"1.6.1170","platform":"windows","loader":"skse","release_channel":"stable","dependency_constraints":[],"conflicts":[],"install_order":[{"item_id":"44444444-4444-4444-8444-444444444444","mod_id":"11111111-1111-4111-8111-111111111111","version_id":"22222222-2222-4222-8222-222222222222","artifact_id":"33333333-3333-4333-8333-333333333333","required":true,"selection_kind":"selected_artifact","decision_reasons":["selected_by_artifact","release_channel:stable","platform:windows","loader:skse"],"download_resolve_endpoint":"/v1/downloads/33333333-3333-4333-8333-333333333333/resolve","sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}],"warnings":[]}}}}}},"/api-keys":{"get":{"tags":["Auth"],"operationId":"listApiKeys","x-moddingflow-stage":"beta","summary":"List the current user's API keys without secrets.","description":"Returns safe key metadata, scopes, resource grants, expiry, last-used, rotation, and revoke state. Raw secrets are never returned by this operation.","security":[{"OAuth2Pkce":["profile:read"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"responses":{"200":{"description":"Safe API-key metadata.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"keys":[{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Example Mod","display_prefix":"mfp_example01","fingerprint":"mfp_example01","scopes":["openid"],"resources":[{"resource_kind":"mod","resource_id":"550e8400-e29b-41d4-a716-446655440000"}],"resource_scope_mode":"all","last_used_at":"2026-07-11T10:30:00Z","expires_at":"2026-07-11T10:30:00Z","revoked_at":"2026-07-11T10:30:00Z","revoked_reason":"example-value","rotation_generation":0,"created_at":"2026-07-11T10:30:00Z"}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listApiKeys","parameters":{"path":{},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"keys":[{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Example Mod","display_prefix":"mfp_example01","fingerprint":"mfp_example01","scopes":["openid"],"resources":[{"resource_kind":"mod","resource_id":"550e8400-e29b-41d4-a716-446655440000"}],"resource_scope_mode":"all","last_used_at":"2026-07-11T10:30:00Z","expires_at":"2026-07-11T10:30:00Z","revoked_at":"2026-07-11T10:30:00Z","revoked_reason":"example-value","rotation_generation":0,"created_at":"2026-07-11T10:30:00Z"}]}}}}},"post":{"tags":["Auth"],"operationId":"createApiKey","x-moddingflow-stage":"beta","summary":"Create an individual resource-scoped API key.","description":"Creates a key for the authenticated team member. Publishing scopes require at least one mod/project grant and are capped by that member's role. Idempotency-Key is required. The raw secret is returned exactly once and is never persisted for replay; a completed replay fails closed with idempotency_replay_unavailable and omits operation-shaped committed metadata, so the caller must inspect or revoke the key through the API.","security":[{"OAuth2Pkce":["profile:read"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"},"examples":{"representative":{"summary":"Representative request","value":{"name":"Example Mod","scopes":["openid"],"resources":[],"expires_at":"2026-07-11T10:30:00Z"}}}}}},"responses":{"201":{"description":"API key created; copy the one-time secret now.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyOneTimeSecretResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Example Mod","display_prefix":"mfp_example01","fingerprint":"mfp_example01","scopes":["openid"],"resources":[{"resource_kind":"mod","resource_id":"550e8400-e29b-41d4-a716-446655440000"}],"resource_scope_mode":"all","last_used_at":"2026-07-11T10:30:00Z","expires_at":"2026-07-11T10:30:00Z","revoked_at":"2026-07-11T10:30:00Z","revoked_reason":"example-value","rotation_generation":0,"created_at":"2026-07-11T10:30:00Z","secret":"mfp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","secret_reveal":"one_time"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"createApiKey","parameters":{"path":{},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001"}},"requestBody":{"mediaType":"application/json","value":{"name":"Example Mod","scopes":["openid"],"resources":[],"expires_at":"2026-07-11T10:30:00Z"}},"success":{"status":"201","mediaType":"application/json","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Example Mod","display_prefix":"mfp_example01","fingerprint":"mfp_example01","scopes":["openid"],"resources":[{"resource_kind":"mod","resource_id":"550e8400-e29b-41d4-a716-446655440000"}],"resource_scope_mode":"all","last_used_at":"2026-07-11T10:30:00Z","expires_at":"2026-07-11T10:30:00Z","revoked_at":"2026-07-11T10:30:00Z","revoked_reason":"example-value","rotation_generation":0,"created_at":"2026-07-11T10:30:00Z","secret":"mfp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","secret_reveal":"one_time"}}}}}},"/api-keys/capabilities":{"get":{"tags":["Auth"],"operationId":"getApiKeyCapabilities","x-moddingflow-stage":"beta","summary":"List API-key scopes and accessible publishing projects.","description":"Shows the role-derived scopes available on every mod/project before a user creates a least-privilege key.","security":[{"OAuth2Pkce":["profile:read"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"responses":{"200":{"description":"Available scopes, role mapping, and publishing projects.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCapabilitiesResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"scopes":["openid"],"projects":[{"mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000","role":"owner","scopes":["openid"]}],"roles":{}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getApiKeyCapabilities","parameters":{"path":{},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"scopes":["openid"],"projects":[{"mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000","role":"owner","scopes":["openid"]}],"roles":{}}}}}}},"/api-keys/{api_key_id}":{"delete":{"tags":["Auth"],"operationId":"revokeApiKey","x-moddingflow-stage":"beta","summary":"Revoke an API key by id.","description":"Revocation is idempotent and audit logged. Only safe key id/fingerprint metadata is logged.","security":[{"OAuth2Pkce":["profile:read"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Uuid"},"example":"550e8400-e29b-41d4-a716-446655440000"}],"responses":{"200":{"description":"Key is revoked or was already inactive.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRevocationResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","fingerprint":"example-value","revoked":true,"changed":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"revokeApiKey","parameters":{"path":{"api_key_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","fingerprint":"example-value","revoked":true,"changed":true}}}}}},"/api-keys/{api_key_id}/rotate":{"post":{"tags":["Auth"],"operationId":"rotateApiKey","x-moddingflow-stage":"beta","summary":"Rotate an active API key.","description":"Atomically creates a replacement with the same scopes, grants, and expiry, then revokes the previous key. Idempotency-Key is required and scoped to the authenticated actor plus source api_key_id. The replacement secret is revealed once and is never persisted for replay; an ambiguous or completed replay fails closed without a second rotation.","security":[{"OAuth2Pkce":["profile:read"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Uuid"},"example":"550e8400-e29b-41d4-a716-446655440000"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Replacement key created and previous key revoked.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyOneTimeSecretResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Example Mod","display_prefix":"mfp_example01","fingerprint":"mfp_example01","scopes":["openid"],"resources":[{"resource_kind":"mod","resource_id":"550e8400-e29b-41d4-a716-446655440000"}],"resource_scope_mode":"all","last_used_at":"2026-07-11T10:30:00Z","expires_at":"2026-07-11T10:30:00Z","revoked_at":"2026-07-11T10:30:00Z","revoked_reason":"example-value","rotation_generation":0,"created_at":"2026-07-11T10:30:00Z","secret":"mfp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","secret_reveal":"one_time"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"rotateApiKey","parameters":{"path":{"api_key_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001"}},"requestBody":null,"success":{"status":"201","mediaType":"application/json","value":{"ok":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Example Mod","display_prefix":"mfp_example01","fingerprint":"mfp_example01","scopes":["openid"],"resources":[{"resource_kind":"mod","resource_id":"550e8400-e29b-41d4-a716-446655440000"}],"resource_scope_mode":"all","last_used_at":"2026-07-11T10:30:00Z","expires_at":"2026-07-11T10:30:00Z","revoked_at":"2026-07-11T10:30:00Z","revoked_reason":"example-value","rotation_generation":0,"created_at":"2026-07-11T10:30:00Z","secret":"mfp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","secret_reveal":"one_time"}}}}}},"/mods/{mod_id}/team-members":{"get":{"tags":["Publishing"],"operationId":"listModTeamMembers","x-moddingflow-stage":"beta","summary":"List a publishing project's team roles.","description":"Returns the derived owner and explicitly assigned maintainer, publisher, and reviewer roles. Requires owner-level teams:admin permission for the project.","security":[{"OAuth2Pkce":["teams:admin"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"}],"responses":{"200":{"description":"Publishing project team.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamMemberListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000","members":[{"user_id":"550e8400-e29b-41d4-a716-446655440000","role":"owner","capabilities":["openid"],"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listModTeamMembers","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222"},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000","members":[{"user_id":"550e8400-e29b-41d4-a716-446655440000","role":"owner","capabilities":["openid"],"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}]}}}}}},"/mods/{mod_id}/team-members/{user_id}":{"put":{"tags":["Publishing"],"operationId":"setModTeamMember","x-moddingflow-stage":"beta","summary":"Assign or replace a project team role.","description":"Owner-only role assignment. The owner role is derived and cannot be replaced; team members authenticate and publish with their own identity and key.","security":[{"OAuth2Pkce":["teams:admin"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"name":"user_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Uuid"},"example":"550e8400-e29b-41d4-a716-446655440000"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTeamMemberRequest"},"examples":{"representative":{"summary":"Representative request","value":{"role":"maintainer"}}}}}},"responses":{"200":{"description":"Team role assigned.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamMemberResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"user_id":"550e8400-e29b-41d4-a716-446655440000","role":"owner","capabilities":["openid"],"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z","mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"setModTeamMember","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222","user_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{}},"requestBody":{"mediaType":"application/json","value":{"role":"maintainer"}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"user_id":"550e8400-e29b-41d4-a716-446655440000","role":"owner","capabilities":["openid"],"created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z","mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000"}}}}},"delete":{"tags":["Publishing"],"operationId":"removeModTeamMember","x-moddingflow-stage":"beta","summary":"Remove a project team member.","description":"Owner-only, idempotent team-member removal. Existing resource-scoped keys immediately lose role authorization even if their secret has not expired.","security":[{"OAuth2Pkce":["teams:admin"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"name":"user_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Uuid"},"example":"550e8400-e29b-41d4-a716-446655440000"}],"responses":{"200":{"description":"Team member is absent from the project.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamMemberRemovalResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000","user_id":"550e8400-e29b-41d4-a716-446655440000","removed":true,"changed":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"removeModTeamMember","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222","user_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"mod_id":"550e8400-e29b-41d4-a716-446655440000","project_id":"550e8400-e29b-41d4-a716-446655440000","user_id":"550e8400-e29b-41d4-a716-446655440000","removed":true,"changed":true}}}}}},"/oauth/authorize":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"get":{"tags":["Auth"],"operationId":"authorizeOAuthClient","summary":"Start Authorization Code with PKCE.","description":"Starts an OAuth authorization request for a registered first-party or self-service application. Public clients must use PKCE S256, an exact registered redirect URI, and explicit user consent. User passwords and service-role secrets are never accepted as client credentials.","parameters":[{"name":"response_type","in":"query","required":true,"description":"OAuth response type. Must be code.","schema":{"type":"string","const":"code"},"example":"code"},{"name":"client_id","in":"query","required":true,"description":"Opaque registered OAuth client identifier for a first-party or self-service application.","schema":{"$ref":"#/components/schemas/OAuthClientKey"},"example":"website"},{"name":"redirect_uri","in":"query","required":true,"description":"Exact redirect URI registered for the client.","schema":{"type":"string","format":"uri"},"example":"https://example.invalid/moddingflow-resource"},{"name":"scope","in":"query","required":true,"description":"Space-delimited scope request. Unknown or denied scopes fail closed.","schema":{"type":"string"},"example":"mods:read"},{"name":"state","in":"query","required":true,"description":"Opaque CSRF state returned to the client.","schema":{"type":"string","minLength":8,"maxLength":512},"example":"example-value"},{"name":"code_challenge","in":"query","required":true,"description":"PKCE S256 code challenge.","schema":{"type":"string","minLength":43,"maxLength":128},"example":"example-code-challengexxxxxxxxxxxxxxxxxxxxx"},{"name":"code_challenge_method","in":"query","required":true,"description":"PKCE method. Plain is not allowed.","schema":{"type":"string","const":"S256"},"example":"S256"},{"name":"nonce","in":"query","required":false,"description":"Required when scope includes openid; copied exactly into the signed ID token.","schema":{"type":"string","minLength":8,"maxLength":512},"example":"example-value"}],"responses":{"302":{"description":"Redirects back to the registered redirect URI with code and state, or with an OAuth error.","headers":{"Location":{"description":"Registered redirect URI with query parameters.","schema":{"type":"string","format":"uri"},"example":"https://client.example.invalid/callback?code=example-code&state=example-state"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"security":[],"deprecated":false,"x-moddingflow-operation-example":{"operationId":"authorizeOAuthClient","parameters":{"path":{},"query":{"response_type":"code","client_id":"website","redirect_uri":"https://example.invalid/moddingflow-resource","scope":"mods:read","state":"example-value","code_challenge":"example-code-challengexxxxxxxxxxxxxxxxxxxxx","code_challenge_method":"S256","nonce":"example-value"},"header":{}},"requestBody":null,"success":{"status":"302","headers":{"Location":"https://client.example.invalid/callback?code=example-code&state=example-state"}}}}},"/oauth/device_authorization":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"post":{"tags":["Auth"],"operationId":"createDeviceAuthorization","summary":"Create a device authorization grant.","description":"Creates a hashed device/user-code authorization grant for CLI or desktop fallback login. Registered clients authenticate with client_secret_basic, client_secret_post, or public-client client_id according to their token endpoint auth method. Only clients marked device_flow_allowed may use this endpoint.","security":[{"OAuthClientBasic":[]},{}],"x-oauth-client-auth-methods":["client_secret_basic","client_secret_post","none"],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizationRequest"},"examples":{"representative":{"summary":"Representative request","value":{"client_id":"website","client_secret":"example-value","scope":"mods:read files:download install_plans:resolve"}}}}}},"responses":{"200":{"description":"Device authorization instructions.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizationResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"device_code":"example-valuexxx","user_code":"example-value","verification_uri":"https://example.invalid/moddingflow-resource","verification_uri_complete":"https://example.invalid/moddingflow-resource","expires_in":60,"interval":5}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"createDeviceAuthorization","parameters":{"path":{},"query":{},"header":{}},"requestBody":{"mediaType":"application/x-www-form-urlencoded","value":{"client_id":"website","client_secret":"example-value","scope":"mods:read files:download install_plans:resolve"}},"success":{"status":"200","mediaType":"application/json","value":{"device_code":"example-valuexxx","user_code":"example-value","verification_uri":"https://example.invalid/moddingflow-resource","verification_uri_complete":"https://example.invalid/moddingflow-resource","expires_in":60,"interval":5}}}}},"/oauth/token":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"post":{"tags":["Auth"],"operationId":"exchangeOAuthToken","summary":"Exchange authorization, device, or refresh grants for tokens.","description":"Issues short-lived public API access tokens. Registered clients authenticate with client_secret_basic, client_secret_post, or public-client client_id according to their token endpoint auth method. Refresh token rotation is mandatory and service-role/internal secrets are not viewer credentials.","security":[{"OAuthClientBasic":[]},{}],"x-oauth-client-auth-methods":["client_secret_basic","client_secret_post","none"],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthTokenRequest"},"examples":{"representative":{"summary":"Representative request","value":{"grant_type":"authorization_code","client_id":"website","client_secret":"example-value","code":"example-valuexxx","redirect_uri":"https://example.invalid/moddingflow-resource","code_verifier":"example-code-verifier-abcdefghijklmnopqrstuvwxyz0123456789"}}}}}},"responses":{"200":{"description":"Token response.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"access_token":"example-redacted-token","token_type":"Bearer","expires_in":60,"refresh_token":"example-redacted-token","scope":"mods:read files:download install_plans:resolve","id_token":"example-redacted-token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"exchangeOAuthToken","parameters":{"path":{},"query":{},"header":{}},"requestBody":{"mediaType":"application/x-www-form-urlencoded","value":{"grant_type":"authorization_code","client_id":"website","client_secret":"example-value","code":"example-valuexxx","redirect_uri":"https://example.invalid/moddingflow-resource","code_verifier":"example-code-verifier-abcdefghijklmnopqrstuvwxyz0123456789"}},"success":{"status":"200","mediaType":"application/json","value":{"access_token":"example-redacted-token","token_type":"Bearer","expires_in":60,"refresh_token":"example-redacted-token","scope":"mods:read files:download install_plans:resolve","id_token":"example-redacted-token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token"}}}}},"/oauth/revoke":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"post":{"tags":["Auth"],"operationId":"revokeOAuthToken","summary":"Revoke an OAuth token or PAT/API key.","description":"Revokes refresh tokens, access-token records when tracked, or personal access tokens. Registered clients authenticate with client_secret_basic, client_secret_post, or public-client client_id according to their token endpoint auth method. Revocation is audit logged and idempotent.","security":[{"OAuthClientBasic":[]},{}],"x-oauth-client-auth-methods":["client_secret_basic","client_secret_post","none"],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthRevocationRequest"},"examples":{"representative":{"summary":"Representative request","value":{"token":"example-redacted-token","token_type_hint":"access_token","client_id":"website","client_secret":"example-value"}}}}}},"responses":{"200":{"description":"Token revoked or already inactive.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthRevocationResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"revoked":true}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"revokeOAuthToken","parameters":{"path":{},"query":{},"header":{}},"requestBody":{"mediaType":"application/x-www-form-urlencoded","value":{"token":"example-redacted-token","token_type_hint":"access_token","client_id":"website","client_secret":"example-value"}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"revoked":true}}}}},"/oauth/introspect":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"post":{"tags":["Auth"],"operationId":"introspectOAuthToken","summary":"Introspect a token for trusted internal clients.","description":"Trusted internal clients can inspect active state, subject, audience, client, expiry, and scopes. Browser clients and public desktop clients must not call introspection.","security":[{"TrustedClientAuth":[]}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthIntrospectionRequest"},"examples":{"representative":{"summary":"Representative request","value":{"token":"example-redacted-token","token_type_hint":"access_token"}}}}}},"responses":{"200":{"description":"Token introspection result.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthIntrospectionResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"active":true,"scope":"mods:read files:download install_plans:resolve","client_id":"website","sub":"example-value","aud":"example-value","iss":"https://example.invalid/moddingflow-resource","exp":1,"iat":1,"token_type":"example-redacted-token"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"introspectOAuthToken","parameters":{"path":{},"query":{},"header":{}},"requestBody":{"mediaType":"application/x-www-form-urlencoded","value":{"token":"example-redacted-token","token_type_hint":"access_token"}},"success":{"status":"200","mediaType":"application/json","value":{"active":true,"scope":"mods:read files:download install_plans:resolve","client_id":"website","sub":"example-value","aud":"example-value","iss":"https://example.invalid/moddingflow-resource","exp":1,"iat":1,"token_type":"example-redacted-token"}}}}},"/.well-known/oauth-authorization-server":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"get":{"tags":["Auth"],"operationId":"getOAuthAuthorizationServerMetadata","summary":"Get OAuth authorization server metadata.","description":"Publishes RFC 8414 metadata for OAuth clients. The openid scope is listed only when this deployment has a valid OIDC signing keyring.","responses":{"200":{"description":"OAuth authorization server metadata document.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthAuthorizationServerMetadata"},"examples":{"representative":{"summary":"Representative successful response","value":{"issuer":"https://api.moddingflow.com","authorization_endpoint":"https://api.moddingflow.com/oauth/authorize","device_authorization_endpoint":"https://api.moddingflow.com/oauth/device_authorization","token_endpoint":"https://api.moddingflow.com/oauth/token","revocation_endpoint":"https://api.moddingflow.com/oauth/revoke","introspection_endpoint":"https://api.moddingflow.com/oauth/introspect","response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:token-exchange"],"code_challenge_methods_supported":["S256"],"token_endpoint_auth_methods_supported":["none","client_secret_basic","client_secret_post"],"scopes_supported":["openid"]}}}}}},"500":{"$ref":"#/components/responses/InternalError"}},"security":[],"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getOAuthAuthorizationServerMetadata","parameters":{"path":{},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"issuer":"https://api.moddingflow.com","authorization_endpoint":"https://api.moddingflow.com/oauth/authorize","device_authorization_endpoint":"https://api.moddingflow.com/oauth/device_authorization","token_endpoint":"https://api.moddingflow.com/oauth/token","revocation_endpoint":"https://api.moddingflow.com/oauth/revoke","introspection_endpoint":"https://api.moddingflow.com/oauth/introspect","response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:token-exchange"],"code_challenge_methods_supported":["S256"],"token_endpoint_auth_methods_supported":["none","client_secret_basic","client_secret_post"],"scopes_supported":["openid"]}}}}},"/.well-known/openid-configuration":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"get":{"tags":["Auth"],"operationId":"getOidcDiscovery","summary":"Get OpenID Connect provider metadata.","description":"Publishes OpenID Connect discovery only when this deployment has a valid asymmetric ID-token signing keyring.","responses":{"200":{"description":"OpenID Connect provider metadata document.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcDiscoveryResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"issuer":"https://api.moddingflow.com","authorization_endpoint":"https://api.moddingflow.com/oauth/authorize","device_authorization_endpoint":"https://api.moddingflow.com/oauth/device_authorization","token_endpoint":"https://api.moddingflow.com/oauth/token","revocation_endpoint":"https://api.moddingflow.com/oauth/revoke","introspection_endpoint":"https://api.moddingflow.com/oauth/introspect","jwks_uri":"https://api.moddingflow.com/.well-known/jwks.json","response_types_supported":["code"],"response_modes_supported":["query"],"grant_types_supported":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:token-exchange"],"code_challenge_methods_supported":["S256"],"token_endpoint_auth_methods_supported":["none","client_secret_basic","client_secret_post"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"claims_supported":["iss","sub","aud","exp","iat","nonce"],"scopes_supported":["openid"]}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/InternalError"}},"security":[],"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getOidcDiscovery","parameters":{"path":{},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"issuer":"https://api.moddingflow.com","authorization_endpoint":"https://api.moddingflow.com/oauth/authorize","device_authorization_endpoint":"https://api.moddingflow.com/oauth/device_authorization","token_endpoint":"https://api.moddingflow.com/oauth/token","revocation_endpoint":"https://api.moddingflow.com/oauth/revoke","introspection_endpoint":"https://api.moddingflow.com/oauth/introspect","jwks_uri":"https://api.moddingflow.com/.well-known/jwks.json","response_types_supported":["code"],"response_modes_supported":["query"],"grant_types_supported":["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:token-exchange"],"code_challenge_methods_supported":["S256"],"token_endpoint_auth_methods_supported":["none","client_secret_basic","client_secret_post"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"claims_supported":["iss","sub","aud","exp","iat","nonce"],"scopes_supported":["openid"]}}}}},"/.well-known/jwks.json":{"servers":[{"url":"https://api.moddingflow.com","description":"OAuth issuer namespace for Moddingflow public API clients."}],"get":{"tags":["Auth"],"operationId":"getJsonWebKeySet","summary":"Get OIDC public signing keys.","description":"Publishes only RSA public verification parameters for the active ID-token key and retained overlap keys. Private key parameters are never returned.","responses":{"200":{"description":"JSON Web Key Set containing public verification keys.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/jwk-set+json":{"schema":{"$ref":"#/components/schemas/JsonWebKeySet"},"examples":{"representative":{"summary":"Representative successful response","value":{"keys":[{"kty":"RSA","kid":"example-value","use":"sig","alg":"RS256","n":"example-valuexxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","e":"example-value"}]}}}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/InternalError"}},"security":[],"deprecated":false,"x-moddingflow-operation-example":{"operationId":"getJsonWebKeySet","parameters":{"path":{},"query":{},"header":{}},"requestBody":null,"success":{"status":"200","mediaType":"application/jwk-set+json","value":{"keys":[{"kty":"RSA","kid":"example-value","use":"sig","alg":"RS256","n":"example-valuexxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","e":"example-value"}]}}}}},"/uploads/{upload_id}/parts":{"post":{"tags":["Uploads"],"operationId":"signUploadParts","summary":"Sign upload parts.","description":"Returns short-lived signed URLs for multipart upload part numbers on an existing upload session.","security":[{"OAuth2Pkce":["files:upload"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/UploadId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadPartsRequest"},"examples":{"representative":{"summary":"Representative request","value":{"partNumbers":[1]}}}}}},"responses":{"200":{"description":"Signed upload parts.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadPartsResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"parts":[{"partNumber":1,"url":"https://example.invalid/moddingflow-resource","sizeBytes":1,"expiresIn":1}]}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"signUploadParts","parameters":{"path":{"upload_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"partNumbers":[1]}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"parts":[{"partNumber":1,"url":"https://example.invalid/moddingflow-resource","sizeBytes":1,"expiresIn":1}]}}}}}},"/uploads/{upload_id}/complete":{"post":{"tags":["Uploads"],"operationId":"completeUploadSession","summary":"Complete an upload session.","description":"Completes a single or multipart upload, records a durable async processing job, and returns before malware/security scan, metadata extraction, indexing hooks, or final file promotion need to finish. Clients poll GET /v1/uploads/{upload_id} until ready or failed.","x-moddingflow-async-processing":{"returnsJob":true,"statusEndpoint":"GET /v1/uploads/{upload_id}","longHttpConnectionRequired":false,"jobKind":"upload_processing"},"x-moddingflow-async-response-semantics":{"acceptedStatus":202,"completedStatus":200,"locationHeader":"Location","locationValueSource":"data.job.statusEndpoint","nonTerminalJobStatuses":["queued","processing"],"terminalJobStatuses":["succeeded","rejected","failed","cancelled"],"idempotencyReplay":"derive_status_and_location_from_committed_response"},"x-moddingflow-tolerant-status-additions":{"validated":true,"extensible":true,"values":["202"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_payload_headers_and_numeric_status","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/lib/publicApi/uploadRoutes.server.test.ts; tests/unit/scripts/public-api-openapi-lint.test.ts"},"security":[{"OAuth2Pkce":["files:upload"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/UploadId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteUploadRequest"},"examples":{"representative":{"summary":"Representative request","value":{"expectedHashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sha1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","parts":[{"etag":"W/\"example-etag\"","partNumber":1}]}}}}}},"responses":{"200":{"description":"Upload completed synchronously or reached a terminal processing result.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteUploadResponse"},"examples":{"uploadCompleted":{"summary":"Upload completed without non-terminal processing","value":{"ok":true,"data":{"actualSizeBytes":14230210,"blobId":"550e8400-e29b-41d4-a716-446655440000","contentType":"application/x-7z-compressed","deduped":false,"etag":"\"archive-etag\"","sessionId":"11111111-1111-4111-8111-111111111111","status":"ready","supportCorrelationId":"upl_7f4c2e36bd8a0e9f124c"}}}}}}},"202":{"description":"Upload accepted for asynchronous processing. Poll the required Location until the returned job becomes terminal.","headers":{"Location":{"description":"Required polling endpoint; exactly equals data.job.statusEndpoint in this response.","required":true,"schema":{"type":"string","pattern":"^/v1/uploads/[^/?#]+$"}},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteUploadResponse"},"examples":{"uploadAcceptedForProcessing":{"summary":"Upload accepted and async processing started","value":{"ok":true,"data":{"actualSizeBytes":14230210,"contentType":"application/x-7z-compressed","etag":"\"archive-etag\"","sessionId":"11111111-1111-4111-8111-111111111111","status":"processing","supportCorrelationId":"upl_7f4c2e36bd8a0e9f124c","job":{"id":"job_01JZ7R9M6TT2S8K9E0H3J3E1N2","kind":"upload_processing","resource":{"kind":"upload-session","id":"11111111-1111-4111-8111-111111111111"},"status":"queued","terminal":false,"statusEndpoint":"/v1/uploads/11111111-1111-4111-8111-111111111111","reason":null,"retry":{"retryable":true,"strategy":"worker_retry","retryAfterSeconds":10}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"completeUploadSession","parameters":{"path":{"upload_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"expectedHashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sha1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","md5":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"expectedSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","parts":[{"etag":"W/\"example-etag\"","partNumber":1}]}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"actualSizeBytes":14230210,"blobId":"550e8400-e29b-41d4-a716-446655440000","contentType":"application/x-7z-compressed","deduped":false,"etag":"\"archive-etag\"","sessionId":"11111111-1111-4111-8111-111111111111","status":"ready","supportCorrelationId":"upl_7f4c2e36bd8a0e9f124c"}}}}}},"/uploads/{upload_id}/abort":{"post":{"tags":["Uploads"],"operationId":"abortUploadSession","summary":"Cancel an upload session.","description":"Cancels an active draft/uploading session and cleans staging objects where possible. The /abort path and legacy status=aborted are retained for compatibility; lifecycleStatus=cancelled is canonical. Sessions already validating, scanning, ready, published, failed, or expired are not cancellable.","security":[{"OAuth2Pkce":["files:manage"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/UploadId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"responses":{"200":{"description":"Upload aborted.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbortUploadResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"status":"aborted","lifecycleStatus":"cancelled","supportCorrelationId":"upl_aaaaaaaaaaaaaaaaaaaa"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"abortUploadSession","parameters":{"path":{"upload_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"status":"aborted","lifecycleStatus":"cancelled","supportCorrelationId":"upl_aaaaaaaaaaaaaaaaaaaa"}}}}}},"/mods/{mod_id}/versions/{version_id}/publish":{"post":{"tags":["Publishing"],"operationId":"publishModVersion","summary":"Publish a validated mod version.","description":"Transitions a team-authorized version to published after upload validation has approved the artifact. The operation records the real actor/key id, idempotency, write audit, moderation review, and webhook queue hooks.","security":[{"OAuth2Pkce":["versions:publish"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/ModVersionId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"responses":{"200":{"description":"Version published.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiVersionWriteResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"publishModVersion","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}},"/mods/{mod_id}/versions/{version_id}/rollback":{"post":{"tags":["Publishing"],"operationId":"rollbackModVersion","summary":"Archive a mod version.","description":"Rolls a version back to the archived state through the public publish state machine. The operation is idempotent by Idempotency-Key.","security":[{"OAuth2Pkce":["versions:archive"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/ModId"},{"$ref":"#/components/parameters/ModVersionId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"responses":{"200":{"description":"Version archived.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiVersionWriteResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"rollbackModVersion","parameters":{"path":{"mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"status":1,"data":{"id":"33333333-3333-4333-8333-333333333333","mod_id":"22222222-2222-4222-8222-222222222222","artifact_id":"44444444-4444-4444-8444-444444444444","source_file_id":1,"state":"draft","request_metadata":{"version":"1.6.1170","label":"example-value","release_channel":"stable","changelog":"example-value","file_category":"main","game_slug":"example-value","game_versions":["1.6.1170"],"loader_platforms":["fabric"],"dependencies":[{"kind":"required_mod","target_mod_id":"22222222-2222-4222-8222-222222222222","label":"example-value","url":"https://example.invalid/moddingflow-resource","note":"example-value"}],"requirements":[{"kind":"skse","label":"example-value","detail":"Representative value for the public API contract.","url":"https://example.invalid/moddingflow-resource"}],"build_metadata":{"source":"local","commit_sha":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","tag":"example-value","release":"example-value","repository":"example-value","workflow_run_id":"550e8400-e29b-41d4-a716-446655440000","client":"example-value"},"publish_state":"draft"},"idempotent":true,"upload":{"file_id":1,"create_url":"example-value"}}}}}}},"/agent/tools":{"get":{"tags":["Agent Gateway"],"operationId":"listAgentGatewayTools","summary":"List Agent Gateway tools.","description":"Returns the allowlisted Agent Gateway tool registry, policy sandbox, dry-run defaults, confirmation gates, and MCP facade status.","security":[{"AgentGatewayAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/Traceparent"}],"responses":{"200":{"description":"Agent Gateway tool registry.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"},"X-Moddingflow-Agent-Schema-Version":{"description":"Agent Gateway tool schema version used by this response.","schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentToolRegistryResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"audience":"agent-gateway","dry_run_default":true,"mcp_facade":"example-value","policy":{"arbitrary_url_fetch":false,"download_actions":"example-value","private_data_exfiltration":false,"redaction_in_logs":true},"schema_version":1,"supported_schema_versions":[1],"tools":[{"name":"mods.search","title":"Representative Moddingflow example","mode":"read","requiredScopes":["openid"],"dryRunDefault":true,"confirmationRequiredFor":["example-value"],"description":"Representative value for the public API contract.","schemaVersion":1}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listAgentGatewayTools","parameters":{"path":{},"query":{},"header":{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"audience":"agent-gateway","dry_run_default":true,"mcp_facade":"example-value","policy":{"arbitrary_url_fetch":false,"download_actions":"example-value","private_data_exfiltration":false,"redaction_in_logs":true},"schema_version":1,"supported_schema_versions":[1],"tools":[{"name":"mods.search","title":"Representative Moddingflow example","mode":"read","requiredScopes":["openid"],"dryRunDefault":true,"confirmationRequiredFor":["example-value"],"description":"Representative value for the public API contract.","schemaVersion":1}]}}}}}},"/agent/tools:run":{"post":{"tags":["Agent Gateway"],"operationId":"runAgentGatewayTool","summary":"Run an Agent Gateway tool.","description":"Runs one allowlisted tool with the delegated agent-gateway token. Inputs and model-facing outputs are schema validated, write tools are dry-run by default, and dangerous actions require explicit confirmation tokens.","security":[{"AgentGatewayAccessToken":[]}],"x-agent-gateway":{"audience":"agent-gateway","stageStatus":"stabilizing_tool_contracts","delegation":{"required":true,"kind":"agent_gateway","actorClaims":["actor_user_id","exchanged_by_client_id","subject_token_client_id"],"fullUserSessionTokenAllowed":false,"tokenExchangeGrant":"urn:ietf:params:oauth:grant-type:token-exchange","subjectTokenType":"urn:ietf:params:oauth:token-type:access_token"},"toolScopes":{"mods.search":["agent:run","agent:tools:read"],"mods.get":["agent:run","agent:tools:read"],"install_plans.resolve":["agent:run","agent:tools:read"],"downloads.resolve":["agent:run","agent:tools:download"],"versions.compare":["agent:run","agent:tools:read"],"publisher.draft":["agent:run","agent:tools:publish_draft"],"publisher.validate":["agent:run","agent:tools:publish_draft"]},"confirmationScopes":{"publisher.draft":["agent:tools:write_confirmed"],"publisher.validate":["agent:tools:write_confirmed"]},"securityAlternatives":[{"scheme":"AgentGatewayAccessToken","audience":"agent-gateway","delegationRequired":true,"requiredScopes":["agent:run","agent:tools:read"],"tools":["mods.search","mods.get","install_plans.resolve","versions.compare"]},{"scheme":"AgentGatewayAccessToken","audience":"agent-gateway","delegationRequired":true,"requiredScopes":["agent:run","agent:tools:download"],"tools":["downloads.resolve"]},{"scheme":"AgentGatewayAccessToken","audience":"agent-gateway","delegationRequired":true,"requiredScopes":["agent:run","agent:tools:publish_draft"],"tools":["publisher.draft","publisher.validate"]}]},"parameters":[{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentToolRunRequest"},"examples":{"representative":{"summary":"Representative request","value":{"tool":"mods.search","input":{},"dry_run":true,"schema_version":1,"confirmation_token":"example-redacted-token"}}}}}},"responses":{"200":{"description":"Agent tool result.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"},"X-Moddingflow-Agent-Schema-Version":{"description":"Agent Gateway tool schema version used by this response.","schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentToolRunResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"tool":"mods.search","dry_run":true,"explanation":"example-value","schema_version":1,"data":{}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"runAgentGatewayTool","parameters":{"path":{},"query":{},"header":{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"tool":"mods.search","input":{},"dry_run":true,"schema_version":1,"confirmation_token":"example-redacted-token"}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"tool":"mods.search","dry_run":true,"explanation":"example-value","schema_version":1,"data":{}}}}}}},"/webhook-subscriptions":{"get":{"tags":["Webhooks"],"operationId":"listWebhookSubscriptions","summary":"List webhook subscriptions.","description":"Returns webhook subscriptions owned by the authenticated actor. Requires webhooks:read and never exposes another user subscription or raw signing secret.","security":[{"OAuth2Pkce":["webhooks:read"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Traceparent"}],"responses":{"200":{"description":"Webhook subscriptions owned by the authenticated actor.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","target_url":"https://example.invalid/moddingflow-resource","name":"Example Mod","event_kinds":["mod.updated"],"status":"active","secret_display_prefix":"example-value","signing":{"algorithm":"hmac-sha256","header":"X-Moddingflow-Signature","timestamp_header":"X-Moddingflow-Timestamp","delivery_id_header":"X-Moddingflow-Delivery-Id","payload_format":"Sign `${timestamp}.${raw_body}` and verify the v1 base64url HMAC-SHA256 signature.","tolerance_seconds":300},"signing_secret_version":1,"failure_count":0,"disabled_at":"2026-07-11T10:30:00Z","created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listWebhookSubscriptions","parameters":{"path":{},"query":{"cursor":"v1.example-opaque-cursor","limit":25},"header":{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","target_url":"https://example.invalid/moddingflow-resource","name":"Example Mod","event_kinds":["mod.updated"],"status":"active","secret_display_prefix":"example-value","signing":{"algorithm":"hmac-sha256","header":"X-Moddingflow-Signature","timestamp_header":"X-Moddingflow-Timestamp","delivery_id_header":"X-Moddingflow-Delivery-Id","payload_format":"Sign `${timestamp}.${raw_body}` and verify the v1 base64url HMAC-SHA256 signature.","tolerance_seconds":300},"signing_secret_version":1,"failure_count":0,"disabled_at":"2026-07-11T10:30:00Z","created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}},"post":{"tags":["Webhooks"],"operationId":"createWebhookSubscription","summary":"Create a webhook subscription.","description":"Creates an owner-scoped webhook subscription and returns a one-time raw signing secret. Delivery payloads are signed with HMAC-SHA256 and carry a stable delivery_id for receiver idempotency.","security":[{"OAuth2Pkce":["webhooks:write"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookSubscriptionRequest"},"examples":{"representative":{"summary":"Representative request","value":{"name":"Example Mod","target_url":"https://example.invalid/moddingflow-resource","event_kinds":["mod.updated"],"metadata":{}}}}}}},"responses":{"201":{"description":"Webhook subscription created. The raw signing secret is returned once.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"subscription":{"id":"550e8400-e29b-41d4-a716-446655440000","target_url":"https://example.invalid/moddingflow-resource","name":"Example Mod","event_kinds":["mod.updated"],"status":"active","secret_display_prefix":"example-value","signing":{"algorithm":"hmac-sha256","header":"X-Moddingflow-Signature","timestamp_header":"X-Moddingflow-Timestamp","delivery_id_header":"X-Moddingflow-Delivery-Id","payload_format":"Sign `${timestamp}.${raw_body}` and verify the v1 base64url HMAC-SHA256 signature.","tolerance_seconds":300},"signing_secret_version":1,"failure_count":0,"disabled_at":"2026-07-11T10:30:00Z","created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"},"signing_secret":"whsec_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","signing_secret_version":1}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"createWebhookSubscription","parameters":{"path":{},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"name":"Example Mod","target_url":"https://example.invalid/moddingflow-resource","event_kinds":["mod.updated"],"metadata":{}}},"success":{"status":"201","mediaType":"application/json","value":{"ok":true,"data":{"subscription":{"id":"550e8400-e29b-41d4-a716-446655440000","target_url":"https://example.invalid/moddingflow-resource","name":"Example Mod","event_kinds":["mod.updated"],"status":"active","secret_display_prefix":"example-value","signing":{"algorithm":"hmac-sha256","header":"X-Moddingflow-Signature","timestamp_header":"X-Moddingflow-Timestamp","delivery_id_header":"X-Moddingflow-Delivery-Id","payload_format":"Sign `${timestamp}.${raw_body}` and verify the v1 base64url HMAC-SHA256 signature.","tolerance_seconds":300},"signing_secret_version":1,"failure_count":0,"disabled_at":"2026-07-11T10:30:00Z","created_at":"2026-07-11T10:30:00Z","updated_at":"2026-07-11T10:30:00Z"},"signing_secret":"whsec_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","signing_secret_version":1}}}}}},"/webhook-subscriptions/{subscription_id}/rotate-secret":{"post":{"tags":["Webhooks"],"operationId":"rotateWebhookSubscriptionSecret","summary":"Rotate a webhook subscription signing secret.","description":"Returns a new one-time signing secret. The previous version remains valid only during the bounded overlap window so receivers can deploy both keys without a delivery gap.","security":[{"OAuth2Pkce":["webhooks:write"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/WebhookSubscriptionId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecretRotationRequest"},"examples":{"representative":{"summary":"Representative request","value":{"overlap_seconds":3600}}}}}},"responses":{"200":{"description":"Signing secret rotated. The raw secret is returned once and is never persisted in generic idempotency storage.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecretRotationResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"subscription_id":"550e8400-e29b-41d4-a716-446655440000","signing_secret":"whsec_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","signing_secret_version":2,"secret_display_prefix":"example-value","overlap_expires_at":"2026-07-11T10:30:00Z"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"rotateWebhookSubscriptionSecret","parameters":{"path":{"subscription_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"overlap_seconds":3600}},"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"subscription_id":"550e8400-e29b-41d4-a716-446655440000","signing_secret":"whsec_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","signing_secret_version":2,"secret_display_prefix":"example-value","overlap_expires_at":"2026-07-11T10:30:00Z"}}}}}},"/webhook-deliveries":{"get":{"tags":["Webhooks"],"operationId":"listWebhookDeliveries","summary":"List webhook deliveries.","description":"Returns delivery attempts, retry state, receiver response metadata, and signed payload IDs. Use delivery_id to dedupe retries and manual redelivery.","security":[{"OAuth2Pkce":["webhooks:read"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/WebhookEventKindFilter"},{"$ref":"#/components/parameters/WebhookDeliveryStatusFilter"},{"$ref":"#/components/parameters/CreatedAfter"},{"$ref":"#/components/parameters/NextAttemptBefore"},{"$ref":"#/components/parameters/Traceparent"}],"responses":{"200":{"description":"Webhook delivery attempts and retry state for the authenticated actor.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryListResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","subscription_id":"550e8400-e29b-41d4-a716-446655440000","event_kind":"mod.updated","event_version":1,"delivery_status":"queued","attempt_count":0,"next_attempt_at":"2026-07-11T10:30:00Z","last_attempt_at":"2026-07-11T10:30:00Z","delivered_at":"2026-07-11T10:30:00Z","last_response_status":100,"last_response_latency_ms":0,"payload":{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","spec_version":"1.0","event_id":"550e8400-e29b-41d4-a716-446655440000","kind":"mod.updated","type":"mod.updated","version":1,"created_at":"2026-07-11T10:30:00Z","occurred_at":"2026-07-11T10:30:00Z","subject":{"user_id":"550e8400-e29b-41d4-a716-446655440000","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","upload_session_id":"550e8400-e29b-41d4-a716-446655440000","artifact_id":"44444444-4444-4444-8444-444444444444","job_id":"550e8400-e29b-41d4-a716-446655440000"},"data":{}},"created_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"listWebhookDeliveries","parameters":{"path":{},"query":{"cursor":"v1.example-opaque-cursor","limit":25,"event_kind":"mod.updated","delivery_status":"queued","created_after":"2026-07-11T10:30:00Z","next_attempt_before":"2026-07-11T10:30:00Z"},"header":{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":null,"success":{"status":"200","mediaType":"application/json","value":{"ok":true,"data":{"items":[{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","subscription_id":"550e8400-e29b-41d4-a716-446655440000","event_kind":"mod.updated","event_version":1,"delivery_status":"queued","attempt_count":0,"next_attempt_at":"2026-07-11T10:30:00Z","last_attempt_at":"2026-07-11T10:30:00Z","delivered_at":"2026-07-11T10:30:00Z","last_response_status":100,"last_response_latency_ms":0,"payload":{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","spec_version":"1.0","event_id":"550e8400-e29b-41d4-a716-446655440000","kind":"mod.updated","type":"mod.updated","version":1,"created_at":"2026-07-11T10:30:00Z","occurred_at":"2026-07-11T10:30:00Z","subject":{"user_id":"550e8400-e29b-41d4-a716-446655440000","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","upload_session_id":"550e8400-e29b-41d4-a716-446655440000","artifact_id":"44444444-4444-4444-8444-444444444444","job_id":"550e8400-e29b-41d4-a716-446655440000"},"data":{}},"created_at":"2026-07-11T10:30:00Z"}],"pagination":{"limit":1,"next_cursor":"v1.example-opaque-cursor","order":"example-value"}}}}}}},"/webhook-deliveries/{delivery_id}/redeliver":{"post":{"tags":["Webhooks"],"operationId":"redeliverWebhookDelivery","summary":"Manually redeliver a webhook delivery.","description":"Creates a new queued delivery linked to the source by redelivery_of_delivery_id and root_delivery_id. event_id remains stable so receivers dedupe the business effect while delivery_id identifies this delivery.","security":[{"OAuth2Pkce":["webhooks:write"]},{"BearerAccessToken":[]},{"PersonalAccessToken":[]}],"parameters":[{"$ref":"#/components/parameters/WebhookDeliveryId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Traceparent"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRedeliveryRequest"},"examples":{"representative":{"summary":"Representative request","value":{"reason":"example-value"}}}}}},"responses":{"202":{"description":"Linked manual redelivery created with a new delivery_id and the original event_id.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryResponse"},"examples":{"representative":{"summary":"Representative successful response","value":{"ok":true,"data":{"delivery":{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","subscription_id":"550e8400-e29b-41d4-a716-446655440000","event_kind":"mod.updated","event_version":1,"delivery_status":"queued","attempt_count":0,"next_attempt_at":"2026-07-11T10:30:00Z","last_attempt_at":"2026-07-11T10:30:00Z","delivered_at":"2026-07-11T10:30:00Z","last_response_status":100,"last_response_latency_ms":0,"payload":{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","spec_version":"1.0","event_id":"550e8400-e29b-41d4-a716-446655440000","kind":"mod.updated","type":"mod.updated","version":1,"created_at":"2026-07-11T10:30:00Z","occurred_at":"2026-07-11T10:30:00Z","subject":{"user_id":"550e8400-e29b-41d4-a716-446655440000","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","upload_session_id":"550e8400-e29b-41d4-a716-446655440000","artifact_id":"44444444-4444-4444-8444-444444444444","job_id":"550e8400-e29b-41d4-a716-446655440000"},"data":{}},"created_at":"2026-07-11T10:30:00Z"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"deprecated":false,"x-moddingflow-operation-example":{"operationId":"redeliverWebhookDelivery","parameters":{"path":{"delivery_id":"550e8400-e29b-41d4-a716-446655440000"},"query":{},"header":{"Idempotency-Key":"example-idempotency-key-0001","traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}},"requestBody":{"mediaType":"application/json","value":{"reason":"example-value"}},"success":{"status":"202","mediaType":"application/json","value":{"ok":true,"data":{"delivery":{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","subscription_id":"550e8400-e29b-41d4-a716-446655440000","event_kind":"mod.updated","event_version":1,"delivery_status":"queued","attempt_count":0,"next_attempt_at":"2026-07-11T10:30:00Z","last_attempt_at":"2026-07-11T10:30:00Z","delivered_at":"2026-07-11T10:30:00Z","last_response_status":100,"last_response_latency_ms":0,"payload":{"id":"550e8400-e29b-41d4-a716-446655440000","delivery_id":"550e8400-e29b-41d4-a716-446655440000","spec_version":"1.0","event_id":"550e8400-e29b-41d4-a716-446655440000","kind":"mod.updated","type":"mod.updated","version":1,"created_at":"2026-07-11T10:30:00Z","occurred_at":"2026-07-11T10:30:00Z","subject":{"user_id":"550e8400-e29b-41d4-a716-446655440000","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","upload_session_id":"550e8400-e29b-41d4-a716-446655440000","artifact_id":"44444444-4444-4444-8444-444444444444","job_id":"550e8400-e29b-41d4-a716-446655440000"},"data":{}},"created_at":"2026-07-11T10:30:00Z"}}}}}}}},"components":{"securitySchemes":{"BearerAccessToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Short-lived public API Bearer access token. service-role and internal secrets are not viewer credentials."},"OAuth2Pkce":{"type":"oauth2","description":"Authorization Code with PKCE for first-party and self-service Moddingflow clients. Public desktop/mobile apps use S256 PKCE and explicit consent; token, revoke, trusted introspection, OAuth issuer metadata, and device authorization are defined in the auth lifecycle contract.","flows":{"authorizationCode":{"authorizationUrl":"https://api.moddingflow.com/oauth/authorize","tokenUrl":"https://api.moddingflow.com/oauth/token","scopes":{"openid":"Request a nonce-bound OpenID Connect ID token for the authenticated user.","mods:read":"Read public catalog resources.","mods:write":"Create and update mod shells and drafts.","versions:write":"Create and update mod versions.","versions:publish":"Publish an approved mod version for an allowed project.","versions:archive":"Archive or roll back a mod version for an allowed project.","files:upload":"Create and complete upload sessions.","files:manage":"Cancel uploads and manage files for an allowed project.","files:download":"Resolve downloadable artifacts.","install_plans:resolve":"Resolve Mod Manager install plans.","profile:read":"Read the authenticated user's public profile context.","webhooks:read":"Read webhook subscriptions and delivery metadata when webhook management endpoints are published.","webhooks:write":"Create, update, rotate, and disable webhook subscriptions when webhook management endpoints are published.","private_data:read":"Read private user-owned API data that is never included in public catalog responses.","teams:admin":"Manage project team members and publishing roles.","agent:run":"Run delegated agent flows after token exchange.","agent:tools:read":"Allow read-only Agent Gateway tool calls.","agent:tools:write":"Deprecated compatibility placeholder for older Agent Gateway write grants; use agent:tools:write_confirmed.","admin:*":"Administrative wildcard reserved for trusted internal clients.","agent:tools:download":"Allow safe Agent Gateway download resolution tools.","agent:tools:publish_draft":"Allow Agent Gateway publisher draft and validation tools in dry-run mode.","agent:tools:write_confirmed":"Allow Agent Gateway write-capable tools only after explicit user confirmation."}}},"x-device-authorization-url":"https://api.moddingflow.com/oauth/device_authorization"},"PersonalAccessToken":{"type":"http","scheme":"bearer","bearerFormat":"PAT","description":"Individual automation API key. The secret is revealed once; runtime storage keeps only token_hash, a safe fingerprint, scopes, resource grants, last_used_at, expiry, rotation/revoke state, and audit events."},"TrustedClientAuth":{"type":"http","scheme":"bearer","bearerFormat":"opaque bearer token","description":"Trusted internal bearer credential for token introspection and agent gateway boundaries."},"OAuthClientBasic":{"type":"http","scheme":"basic","description":"OAuth client_id/client_secret authentication for confidential revocation clients. Registered client_secret_post and public-client none alternatives use the revocation form fields documented by OAuthRevocationRequest."},"AgentGatewayAccessToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Short-lived delegated Agent Gateway token. The token must carry aud=agent-gateway, agent scopes, and actor/delegation metadata; it is not a full public API session token."}},"parameters":{"Cursor":{"name":"cursor","in":"query","required":false,"description":"Opaque v1. keyset pagination cursor returned by the previous page. Treat it as an unparsed base64url token and echo it back only for the same resource, normalized filters, and sort. Incompatible or malformed reuse returns 400 invalid_input; rows inserted after page one stay outside the cursor snapshot.","schema":{"type":"string","minLength":1,"maxLength":512},"example":"v1.example-opaque-cursor"},"Limit":{"name":"limit","in":"query","required":false,"description":"Page size. The maximum public API page size is 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"example":25},"GameSlug":{"name":"game_slug","in":"query","required":false,"description":"Stable game slug filter. The website compatibility namespace also accepts game and gameSlug aliases.","schema":{"$ref":"#/components/schemas/Slug"},"example":"example-value"},"SearchQuery":{"name":"q","in":"query","required":false,"description":"Optional catalog search text.","schema":{"type":"string","minLength":1,"maxLength":160},"example":"sky ui"},"Sort":{"name":"sort","in":"query","required":false,"description":"Catalog ordering mode.","schema":{"type":"string","enum":["relevance","latest","trending","downloads"],"default":"relevance"},"example":"relevance"},"ModId":{"name":"mod_id","in":"path","required":true,"description":"Stable public mod UUID.","schema":{"$ref":"#/components/schemas/ModId"},"example":"22222222-2222-4222-8222-222222222222"},"ArtifactId":{"name":"artifact_id","in":"path","required":true,"description":"Stable public artifact UUID.","schema":{"$ref":"#/components/schemas/ArtifactId"},"example":"44444444-4444-4444-8444-444444444444"},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"description":"Client-generated retry key for write/upload operations. Reusing a key with a different body returns a conflict.","schema":{"type":"string","minLength":8,"maxLength":160},"example":"example-idempotency-key-0001"},"OptionalIdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional retry key for deterministic resolve operations.","schema":{"type":"string","minLength":8,"maxLength":160},"example":"example-idempotency-key-0001"},"IfMatch":{"name":"If-Match","in":"header","required":true,"description":"Exactly one strong ETag returned by an authenticated Mod or ModVersion detail/create/update response. Weak validators, wildcard, and lists fail with 412; omission fails with 428.","schema":{"type":"string","minLength":1},"example":"\"example-etag\""},"IfNoneMatch":{"name":"If-None-Match","in":"header","required":false,"description":"Revalidate an anonymous public catalog representation. A weak match returns 304 with no body; credential-bearing requests are private no-store and do not use the public validator.","schema":{"type":"string","minLength":1},"example":"W/\"example-etag\""},"Traceparent":{"name":"traceparent","in":"header","required":false,"description":"W3C trace context header accepted for distributed tracing.","schema":{"type":"string","minLength":1,"maxLength":128},"example":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},"GamePathSlug":{"name":"game_slug","in":"path","required":true,"description":"Stable game slug.","schema":{"$ref":"#/components/schemas/Slug"},"example":"example-value"},"ModIdOrSlug":{"name":"mod_id","in":"path","required":true,"description":"Stable public mod UUID or canonical public slug for direct catalog lookup.","schema":{"anyOf":[{"$ref":"#/components/schemas/ModId"},{"$ref":"#/components/schemas/Slug"}]},"example":"22222222-2222-4222-8222-222222222222"},"ModVersionId":{"name":"version_id","in":"path","required":true,"description":"Stable public mod-version UUID.","schema":{"$ref":"#/components/schemas/VersionId"},"example":"33333333-3333-4333-8333-333333333333"},"GameVersion":{"name":"game_version","in":"query","required":false,"description":"Game version facet filter such as 1.6.1170. The website compatibility namespace also accepts gameVersion and version aliases.","schema":{"type":"string","minLength":1,"maxLength":80},"example":"1.6.1170"},"Loader":{"name":"loader","in":"query","required":false,"description":"Loader/platform facet filter such as skse, fabric, forge, or a game-specific loader key.","schema":{"type":"string","minLength":1,"maxLength":80},"example":"example-value"},"Category":{"name":"category","in":"query","required":false,"description":"Catalog category slug filter.","schema":{"$ref":"#/components/schemas/Slug"},"example":"example-value"},"Language":{"name":"lang","in":"query","required":false,"description":"Preferred label language for category and facet labels.","schema":{"type":"string","minLength":2,"maxLength":16,"default":"en"},"example":"en"},"RequiredSearchQuery":{"name":"q","in":"query","required":true,"description":"Catalog search text.","schema":{"type":"string","minLength":1,"maxLength":160},"example":"sky ui"},"UploadId":{"name":"upload_id","in":"path","required":true,"description":"Stable public upload session UUID.","schema":{"$ref":"#/components/schemas/UploadId"},"example":"550e8400-e29b-41d4-a716-446655440000"},"WebhookSubscriptionId":{"name":"subscription_id","in":"path","required":true,"description":"Stable public webhook subscription UUID.","schema":{"$ref":"#/components/schemas/WebhookSubscriptionId"},"example":"550e8400-e29b-41d4-a716-446655440000"},"WebhookDeliveryId":{"name":"delivery_id","in":"path","required":true,"description":"Stable public webhook delivery UUID used by receivers for idempotency.","schema":{"$ref":"#/components/schemas/WebhookDeliveryId"},"example":"550e8400-e29b-41d4-a716-446655440000"},"WebhookEventKindFilter":{"name":"event_kind","in":"query","required":false,"description":"Filter webhook deliveries by stable event name.","schema":{"$ref":"#/components/schemas/WebhookEventKind"},"example":"mod.updated"},"WebhookDeliveryStatusFilter":{"name":"delivery_status","in":"query","required":false,"description":"Filter webhook deliveries by delivery status.","schema":{"$ref":"#/components/schemas/WebhookDeliveryStatus"},"example":"queued"},"CreatedAfter":{"name":"created_after","in":"query","required":false,"description":"Return resources created after this timestamp.","schema":{"type":"string","format":"date-time"},"example":"2026-07-11T10:30:00Z"},"NextAttemptBefore":{"name":"next_attempt_before","in":"query","required":false,"description":"Return webhook deliveries whose next retry is due before this timestamp.","schema":{"type":"string","format":"date-time"},"example":"2026-07-11T10:30:00Z"}},"headers":{"ETag":{"description":"Weak anonymous representation validator for If-None-Match, or a strong authenticated Mod/ModVersion write validator for If-Match. Do not interchange the two.","schema":{"type":"string"}},"CacheControl":{"description":"Anonymous catalog responses may be retained but require origin revalidation on every reuse; credential-bearing responses are private no-store.","schema":{"type":"string"}},"Vary":{"description":"Catalog cache-key dimensions that isolate anonymous responses from Authorization or Cookie requests.","schema":{"type":"string"},"example":"Accept-Encoding, Authorization, Cookie"},"XRequestId":{"description":"Server-generated request id also present in error envelopes.","schema":{"type":"string"}},"XUploadCorrelationId":{"description":"Pseudonymous upload lifecycle handle that is safe to include in a support ticket. It is not an authorization credential.","schema":{"$ref":"#/components/schemas/UploadSupportCorrelationId"}},"RetryAfter":{"description":"Seconds the client should wait before retrying a rate-limited request.","schema":{"type":"integer","minimum":1},"example":60},"RateLimitLimit":{"description":"Quota limit for the active public API rate-limit bucket.","schema":{"type":"integer","minimum":1},"example":120},"RateLimitRemaining":{"description":"Remaining quota in the active public API rate-limit bucket.","schema":{"type":"integer","minimum":0},"example":0},"RateLimitReset":{"description":"Seconds until the active public API rate-limit bucket resets.","schema":{"type":"integer","minimum":1},"example":60},"Deprecation":{"description":"RFC 9745 deprecation date for a deprecated resource or capability, encoded as an HTTP Structured Field date.","schema":{"type":"string","pattern":"^@[0-9]+$"},"example":"@1735689600"},"Sunset":{"description":"RFC 8594 HTTP-date after which the deprecated resource or capability is expected to become unavailable.","schema":{"type":"string"},"example":"Wed, 01 Apr 2026 00:00:00 GMT"},"Link":{"description":"Web Linking header used with rel=\"deprecation\" or rel=\"sunset\" to point clients at migration guidance.","schema":{"type":"string"},"example":"<https://moddingflow.com/api/?section=reference-sdk-policy>; rel=\"deprecation\"; type=\"text/html\""}},"responses":{"CatalogNotModified":{"description":"The anonymous public catalog representation matches If-None-Match. The response has no body.","headers":{"Cache-Control":{"$ref":"#/components/headers/CacheControl"},"ETag":{"$ref":"#/components/headers/ETag"},"Vary":{"$ref":"#/components/headers/Vary"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}}},"BadRequest":{"description":"Request syntax or query parameters are invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"invalidRequest":{"summary":"Invalid request syntax","value":{"type":"https://api.moddingflow.com/problems/invalid_request","title":"Invalid request","status":400,"detail":"Request body must be valid JSON.","instance":"urn:moddingflow:request:req_01JZ7Q3J6WQX8P6M0K6W0R5B7A","ok":false,"code":"invalid_request","machine_code":"invalid_request","http_status":400,"request_id":"req_01JZ7Q3J6WQX8P6M0K6W0R5B7A","trace_id":"trace_01JZ7Q3J6WQX8P6M0K6W0R5B7A","retryable":false,"docs_slug":"errors.invalid_request","error":{"machine_code":"invalid_request","http_status":400,"message":"Request body must be valid JSON.","request_id":"req_01JZ7Q3J6WQX8P6M0K6W0R5B7A","trace_id":"trace_01JZ7Q3J6WQX8P6M0K6W0R5B7A"}}}}}}},"Unauthorized":{"description":"Authentication is missing, expired, or invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"unauthorized":{"summary":"Missing or invalid bearer token","value":{"type":"https://api.moddingflow.com/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Bearer token is required.","instance":"urn:moddingflow:request:req_01JZ7Q3QYBBYB9C4T1E8VNW3YJ","ok":false,"code":"unauthorized","machine_code":"unauthorized","http_status":401,"request_id":"req_01JZ7Q3QYBBYB9C4T1E8VNW3YJ","trace_id":"trace_01JZ7Q3QYBBYB9C4T1E8VNW3YJ","retryable":false,"docs_slug":"errors.unauthorized","error":{"machine_code":"unauthorized","http_status":401,"message":"Bearer token is required.","request_id":"req_01JZ7Q3QYBBYB9C4T1E8VNW3YJ","trace_id":"trace_01JZ7Q3QYBBYB9C4T1E8VNW3YJ"}}}}}}},"Forbidden":{"description":"The token or public client lacks the required scope.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"forbidden":{"summary":"Missing required scope","value":{"type":"https://api.moddingflow.com/problems/forbidden","title":"Forbidden","status":403,"detail":"Required scope is missing.","instance":"urn:moddingflow:request:req_01JZ7Q3X2YQ0S6C6ZPFTW2SMX2","ok":false,"code":"forbidden","machine_code":"forbidden","http_status":403,"request_id":"req_01JZ7Q3X2YQ0S6C6ZPFTW2SMX2","trace_id":"trace_01JZ7Q3X2YQ0S6C6ZPFTW2SMX2","retryable":false,"required_scopes":["mods:write"],"docs_slug":"errors.forbidden","error":{"machine_code":"forbidden","http_status":403,"message":"Required scope is missing.","request_id":"req_01JZ7Q3X2YQ0S6C6ZPFTW2SMX2","trace_id":"trace_01JZ7Q3X2YQ0S6C6ZPFTW2SMX2"}}}}}}},"NotFound":{"description":"The requested public resource was not found or is not visible.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"notFound":{"summary":"Public resource not found","value":{"type":"https://api.moddingflow.com/problems/not_found","title":"Resource not found","status":404,"detail":"The requested public resource was not found.","instance":"urn:moddingflow:request:req_01JZ7Q43P31JSQQF7W3R24H0ED","ok":false,"code":"not_found","machine_code":"not_found","http_status":404,"request_id":"req_01JZ7Q43P31JSQQF7W3R24H0ED","trace_id":"trace_01JZ7Q43P31JSQQF7W3R24H0ED","retryable":false,"docs_slug":"errors.not_found","error":{"machine_code":"not_found","http_status":404,"message":"The requested public resource was not found.","request_id":"req_01JZ7Q43P31JSQQF7W3R24H0ED","trace_id":"trace_01JZ7Q43P31JSQQF7W3R24H0ED"}}}}}}},"Conflict":{"description":"The operation conflicts with current state or idempotency history.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"idempotencyConflict":{"summary":"Idempotency key reused with a different body","value":{"type":"https://api.moddingflow.com/problems/idempotency_conflict","title":"Idempotency conflict","status":409,"detail":"The Idempotency-Key was already used with a different request body.","instance":"urn:moddingflow:request:req_01JZ7Q4AT5GHZXC0KNEDZ2YNSF","ok":false,"code":"idempotency_conflict","machine_code":"idempotency_conflict","http_status":409,"request_id":"req_01JZ7Q4AT5GHZXC0KNEDZ2YNSF","trace_id":"trace_01JZ7Q4AT5GHZXC0KNEDZ2YNSF","retryable":false,"docs_slug":"errors.idempotency_conflict","error":{"machine_code":"idempotency_conflict","http_status":409,"message":"The Idempotency-Key was already used with a different request body.","request_id":"req_01JZ7Q4AT5GHZXC0KNEDZ2YNSF","trace_id":"trace_01JZ7Q4AT5GHZXC0KNEDZ2YNSF"}}}}}}},"Gone":{"description":"The requested session or expiring capability is no longer usable.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"gone":{"summary":"Expired session or capability","value":{"type":"https://api.moddingflow.com/problems/gone","title":"Capability expired","status":410,"detail":"The requested session is no longer usable.","instance":"urn:moddingflow:request:req_01JZ7Q4HZD7JQ1B5WDRVD1ZK7E","ok":false,"code":"gone","machine_code":"gone","http_status":410,"request_id":"req_01JZ7Q4HZD7JQ1B5WDRVD1ZK7E","trace_id":"trace_01JZ7Q4HZD7JQ1B5WDRVD1ZK7E","retryable":false,"docs_slug":"errors.gone","error":{"machine_code":"gone","http_status":410,"message":"The requested session is no longer usable.","request_id":"req_01JZ7Q4HZD7JQ1B5WDRVD1ZK7E","trace_id":"trace_01JZ7Q4HZD7JQ1B5WDRVD1ZK7E"}}}}}}},"PreconditionFailed":{"description":"The required If-Match precondition failed.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"preconditionFailed":{"summary":"If-Match precondition failed","value":{"type":"https://api.moddingflow.com/problems/precondition_failed","title":"Precondition failed","status":412,"detail":"The supplied If-Match value no longer matches the resource.","instance":"urn:moddingflow:request:req_01JZ7Q4QDFHAXR9B4V9VXEP39M","ok":false,"code":"precondition_failed","machine_code":"precondition_failed","http_status":412,"request_id":"req_01JZ7Q4QDFHAXR9B4V9VXEP39M","trace_id":"trace_01JZ7Q4QDFHAXR9B4V9VXEP39M","retryable":false,"docs_slug":"errors.precondition_failed","error":{"machine_code":"precondition_failed","http_status":412,"message":"The supplied If-Match value no longer matches the resource.","request_id":"req_01JZ7Q4QDFHAXR9B4V9VXEP39M","trace_id":"trace_01JZ7Q4QDFHAXR9B4V9VXEP39M"}}}}}}},"PreconditionRequired":{"description":"The mutable resource requires an If-Match validator.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"preconditionRequired":{"summary":"If-Match is required","value":{"type":"https://api.moddingflow.com/problems/precondition_required","title":"Precondition required","status":428,"detail":"If-Match is required for mutable Mod and ModVersion writes.","instance":"urn:moddingflow:request:req_01JZ7Q4REQUIRED","ok":false,"code":"precondition_required","machine_code":"precondition_required","http_status":428,"request_id":"req_01JZ7Q4REQUIRED","trace_id":"trace_01JZ7Q4REQUIRED","retryable":false,"docs_slug":"errors.precondition_required","error":{"machine_code":"precondition_required","http_status":428,"message":"If-Match is required for mutable Mod and ModVersion writes.","request_id":"req_01JZ7Q4REQUIRED","trace_id":"trace_01JZ7Q4REQUIRED"}}}}}}},"ValidationFailed":{"description":"Field-level validation failed.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"validationFailed":{"summary":"Field validation failed","value":{"type":"https://api.moddingflow.com/problems/validation_failed","title":"Validation failed","status":422,"detail":"One or more fields are invalid.","instance":"urn:moddingflow:request:req_01JZ7Q4XEKGQQ80P7RXMPVXMH6","ok":false,"code":"validation_failed","machine_code":"validation_failed","http_status":422,"request_id":"req_01JZ7Q4XEKGQQ80P7RXMPVXMH6","trace_id":"trace_01JZ7Q4XEKGQQ80P7RXMPVXMH6","retryable":false,"fields":[{"field":"game_version","code":"unsupported_version","message":"This game version is not supported for the selected mod."}],"docs_slug":"errors.validation_failed","error":{"machine_code":"validation_failed","http_status":422,"message":"One or more fields are invalid.","fields":[{"field":"game_version","code":"unsupported_version","message":"This game version is not supported for the selected mod."}],"request_id":"req_01JZ7Q4XEKGQQ80P7RXMPVXMH6","trace_id":"trace_01JZ7Q4XEKGQQ80P7RXMPVXMH6"}}}}}}},"RateLimited":{"description":"Rate limit exceeded.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"rateLimited":{"summary":"Request bucket exhausted","value":{"type":"https://api.moddingflow.com/problems/rate_limited","title":"Rate limit exceeded","status":429,"detail":"Public API rate limit exceeded before side effects.","instance":"urn:moddingflow:request:req_01JZ7Q54FMQNEJCW8CRK5VHM1C","ok":false,"code":"rate_limited","machine_code":"rate_limited","http_status":429,"request_id":"req_01JZ7Q54FMQNEJCW8CRK5VHM1C","trace_id":"trace_01JZ7Q54FMQNEJCW8CRK5VHM1C","retryable":true,"retry_after_seconds":60,"quota_bucket":"catalog.search.ip","docs_slug":"errors.rate_limited","error":{"machine_code":"rate_limited","http_status":429,"message":"Public API rate limit exceeded before side effects.","request_id":"req_01JZ7Q54FMQNEJCW8CRK5VHM1C","trace_id":"trace_01JZ7Q54FMQNEJCW8CRK5VHM1C"}}}}}}},"InternalError":{"description":"Unexpected server error.","headers":{"X-Request-Id":{"$ref":"#/components/headers/XRequestId"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsError"},"examples":{"internalError":{"summary":"Unexpected server failure","value":{"type":"https://api.moddingflow.com/problems/internal_error","title":"Internal server error","status":500,"detail":"Unexpected server error.","instance":"urn:moddingflow:request:req_01JZ7Q5B0VNX4D8S55A1PV39H7","ok":false,"code":"internal_error","machine_code":"internal_error","http_status":500,"request_id":"req_01JZ7Q5B0VNX4D8S55A1PV39H7","trace_id":"trace_01JZ7Q5B0VNX4D8S55A1PV39H7","retryable":true,"docs_slug":"errors.internal_error","error":{"machine_code":"internal_error","http_status":500,"message":"Unexpected server error.","request_id":"req_01JZ7Q5B0VNX4D8S55A1PV39H7","trace_id":"trace_01JZ7Q5B0VNX4D8S55A1PV39H7"}}}}}}}},"schemas":{"Uuid":{"type":"string","format":"uuid"},"ModId":{"allOf":[{"$ref":"#/components/schemas/Uuid"}],"example":"22222222-2222-4222-8222-222222222222","description":"Stable public mod UUID."},"VersionId":{"allOf":[{"$ref":"#/components/schemas/Uuid"}],"example":"33333333-3333-4333-8333-333333333333","description":"Stable public mod version UUID."},"ArtifactId":{"allOf":[{"$ref":"#/components/schemas/Uuid"}],"example":"44444444-4444-4444-8444-444444444444","description":"Stable public artifact UUID."},"UploadId":{"allOf":[{"$ref":"#/components/schemas/Uuid"}],"description":"Stable public upload session UUID."},"UploadSupportCorrelationId":{"type":"string","pattern":"^upl_[a-f0-9]{20}$","description":"Pseudonymous support-safe upload handle. It contains no user, filename, storage-key, API-key, or token material and grants no access."},"Slug":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{1,80}$"},"LocalizedStringMap":{"type":"object","description":"Localized text keyed by BCP 47 language tags supported by the site.","additionalProperties":{"type":"string"},"minProperties":1},"PaginationMeta":{"type":"object","required":["limit","next_cursor","order"],"properties":{"limit":{"type":"integer","minimum":1,"maximum":100},"next_cursor":{"type":["string","null"]},"order":{"type":"string","description":"Stable ordering contract for this page."}}},"SuccessEnvelope":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object"}}},"ProblemDetailsError":{"type":"object","description":"RFC 9457 Problem Details response for Moddingflow public API errors. The code/error fields are compatibility extensions for existing clients.","required":["type","title","status","detail","instance","ok","code","error"],"properties":{"type":{"type":"string","format":"uri","description":"Stable problem type URI under https://api.moddingflow.com/problems/{code}."},"title":{"type":"string","description":"Short, stable summary for this problem type."},"status":{"type":"integer","minimum":400,"maximum":599,"description":"HTTP status code repeated in the Problem Details body."},"detail":{"type":"string","description":"Human-readable detail for this occurrence. Clients should branch on code or type, not detail."},"instance":{"type":"string","description":"Opaque occurrence URI for this request, derived from the request id."},"ok":{"type":"boolean","const":false,"description":"Compatibility bridge with the previous { ok, code, error } envelope."},"code":{"type":"string","description":"Machine-readable compatibility code. Matches machine_code and error.machine_code."},"machine_code":{"type":"string","pattern":"^[a-z][a-z0-9_]{2,80}$"},"http_status":{"type":"integer","minimum":400,"maximum":599},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FieldError"}},"trace_id":{"type":"string"},"request_id":{"type":"string"},"retryable":{"type":"boolean","description":"Whether retrying the same logical request is safe after backoff or the documented server delay."},"retry_after_seconds":{"type":"integer","minimum":0,"maximum":86400,"description":"Bounded server-provided delay before retry. Present only for retryable errors when known and always on 429."},"quota_bucket":{"type":"string","pattern":"^[a-z][a-z_.:-]{2,119}$","description":"Stable non-sensitive quota bucket class. Never contains an IP, token, user id, or raw storage key."},"download_job":{"$ref":"#/components/schemas/DownloadJob","description":"Safe durable download state returned on download-specific 429 responses. Secrets, actor identity, and provider object paths are never disclosed."},"required_scopes":{"type":"array","items":{"$ref":"#/components/schemas/ScopeString"},"uniqueItems":true,"description":"Minimum scopes that can make this exact authorization decision pass."},"docs_slug":{"type":"string","pattern":"^errors\\.[a-z][a-z0-9_]{2,80}$","description":"Stable lookup key in the global error registry and docs/public-api/ERRORS.md; not a URL."},"error":{"$ref":"#/components/schemas/PublicApiError"}}},"ErrorEnvelope":{"type":"object","required":["ok","code","error"],"properties":{"ok":{"type":"boolean","const":false,"description":"Compatibility bridge with current { ok, code } site envelopes."},"code":{"type":"string","description":"Compatibility bridge code. Matches error.machine_code."},"error":{"$ref":"#/components/schemas/PublicApiError"}}},"PublicApiError":{"type":"object","required":["machine_code","http_status","message","trace_id","request_id"],"properties":{"machine_code":{"type":"string","pattern":"^[a-z][a-z0-9_]{2,80}$"},"http_status":{"type":"integer","minimum":400,"maximum":599},"message":{"type":"string"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FieldError"}},"trace_id":{"type":"string"},"request_id":{"type":"string"}}},"FieldError":{"type":"object","required":["field","code","message"],"properties":{"field":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}}},"PublicMod":{"type":"object","required":["id","slug","game_slug","title","summary","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/ModId"},"game_id":{"$ref":"#/components/schemas/Uuid"},"slug":{"$ref":"#/components/schemas/Slug"},"localized_slugs":{"type":"object","description":"Localized route slugs keyed by language code. Clients should persist id and treat slugs as display aliases.","additionalProperties":{"type":"string"}},"game_slug":{"$ref":"#/components/schemas/Slug"},"title":{"$ref":"#/components/schemas/LocalizedStringMap"},"summary":{"$ref":"#/components/schemas/LocalizedStringMap"},"description":{"$ref":"#/components/schemas/LocalizedStringMap"},"latest_version":{"type":["string","null"]},"visibility":{"$ref":"#/components/schemas/PublicVisibility"},"access_tier":{"$ref":"#/components/schemas/PublicAccessTier"},"is_nsfw":{"type":"boolean"},"homepage_url":{"type":["string","null"],"format":"uri"},"source_url":{"type":["string","null"],"format":"uri"},"download_count":{"type":"integer","minimum":0},"like_count":{"type":"integer","minimum":0},"view_count":{"type":"integer","minimum":0},"categories":{"type":"array","items":{"$ref":"#/components/schemas/PublicCatalogCategory"}},"facets":{"type":"array","items":{"$ref":"#/components/schemas/PublicCatalogFacet"}},"loaders":{"type":"array","items":{"type":"string"}},"game_versions":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"links":{"type":"object","properties":{"web":{"type":"string","format":"uri"},"versions":{"type":"string"}}}}},"PublicModVersion":{"type":"object","required":["id","mod_id","version","release_channel","artifact_ids","published_at"],"properties":{"id":{"$ref":"#/components/schemas/VersionId"},"mod_id":{"$ref":"#/components/schemas/ModId"},"primary_artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"version":{"type":"string","minLength":1,"maxLength":80},"semantic_version":{"type":["string","null"],"description":"Parsed semantic version when the source value follows the supported SemVer subset; otherwise null while version remains available as version."},"release_channel":{"type":"string","enum":["stable","beta","alpha","experimental"]},"game_versions":{"type":"array","items":{"type":"string"}},"loaders":{"type":"array","items":{"type":"string"}},"artifact_ids":{"type":"array","items":{"$ref":"#/components/schemas/ArtifactId"}},"artifacts":{"type":"array","description":"Additive expanded logical-artifact metadata. When present, its ordered ids exactly match artifact_ids and primary_artifact_id is a member. It is omitted for older deployments and for legacy artifacts without current managed size, SHA-256, AV-scan, and resolve-eligibility evidence.","items":{"$ref":"#/components/schemas/PublicModArtifactDetails"},"example":[{"id":"44444444-4444-4444-8444-444444444444","mod_id":"22222222-2222-4222-8222-222222222222","version_id":"33333333-3333-4333-8333-333333333333","file_kind":"main","label":"Main archive","original_filename":"mod-1.0.0.zip","content_type":"application/zip","game_version_key":"1.6.1170","loader_key":null,"size_bytes":1048576,"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hashes":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"artifact_source":"r2_blob","status":"published","scan_status":"clean","download_metadata":{"resolve_endpoint":"/v1/downloads/44444444-4444-4444-8444-444444444444/resolve","range_supported":true}}]},"has_immutable_artifact_binding":{"type":"boolean","description":"True when the version is backed by storage_blobs through an immutable artifact row."},"changelog":{"type":"array","items":{"$ref":"#/components/schemas/PublicChangelogEntry"}},"request_metadata":{"$ref":"#/components/schemas/VersionRequestMetadata"},"published_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"PublicVisibility":{"type":"string","enum":["public","unlisted","private","test_mode"]},"PublicAccessTier":{"type":"string","enum":["public","restricted","paid"]},"PublicGame":{"type":"object","required":["id","slug","title","is_enabled"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"slug":{"$ref":"#/components/schemas/Slug"},"title":{"$ref":"#/components/schemas/LocalizedStringMap"},"icon_path":{"type":"string"},"is_enabled":{"type":"boolean"},"sort_order":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"DomainEntityKind":{"type":"string","enum":["Game","Mod","ModVersion","Artifact","Blob","UploadSession","DownloadGrant","User","Team","Dependency","Job","WebhookSubscription","WebhookDelivery"]},"DomainAvailabilityState":{"type":"string","enum":["active","archived","tombstoned"]},"VersionLifecycleState":{"type":"string","enum":["draft","validating","scanning","ready","published","archived","withdrawn","failed"],"description":"Canonical ModVersion lifecycle. Legacy uploaded/quarantined/approved/rejected values remain accepted as additive compatibility aliases."},"PublicIdentityLifecycle":{"type":"object","required":["id","entity_kind","availability_state","lifecycle_version"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"entity_kind":{"$ref":"#/components/schemas/DomainEntityKind"},"availability_state":{"$ref":"#/components/schemas/DomainAvailabilityState"},"lifecycle_version":{"type":"integer","format":"int64","minimum":1,"description":"Optimistic concurrency version. A stale expected value fails with lifecycle_version_conflict."},"tombstoned_at":{"type":["string","null"],"format":"date-time"}}},"PublicUser":{"type":"object","required":["id","username","state","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"username":{"type":"string","minLength":1,"maxLength":80},"display_name":{"type":["string","null"],"maxLength":120},"state":{"type":"string","enum":["active","suspended","deleted","tombstoned"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"description":"Privacy-minimized public account identity. Email, login credentials, and auth-provider identifiers are never part of this schema."},"PublicTeam":{"type":"object","required":["id","mod_id","owner_user_id","name","state","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"mod_id":{"$ref":"#/components/schemas/ModId"},"owner_user_id":{"oneOf":[{"$ref":"#/components/schemas/Uuid"},{"type":"null"}],"description":"Null only while ownership transfer/account-deletion recovery is pending; capability checks fail closed."},"name":{"type":"string","minLength":1,"maxLength":120},"state":{"type":"string","enum":["active","archived","tombstoned"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"PublicBlob":{"type":"object","required":["id","sha256","size_bytes","status","created_at"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"size_bytes":{"type":"integer","format":"int64","minimum":1},"content_type":{"type":["string","null"]},"status":{"type":"string","enum":["pending","validating","ready","quarantined","rejected","deleted","tombstoned"]},"created_at":{"type":"string","format":"date-time"}},"description":"Provider-neutral immutable byte identity. Bucket, object key, ETag, and filename are deliberately absent."},"PublicJob":{"type":"object","required":["id","kind","state","attempt_count","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"resource_id":{"oneOf":[{"$ref":"#/components/schemas/Uuid"},{"type":"null"}]},"kind":{"type":"string","minLength":1,"maxLength":80},"state":{"type":"string","enum":["queued","processing","succeeded","rejected","failed","cancelled","tombstoned"]},"attempt_count":{"type":"integer","minimum":0},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"PublicBuild":{"type":"object","required":["id","slug","game_slug","title","visibility","access_tier"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"slug":{"$ref":"#/components/schemas/Slug"},"game_slug":{"$ref":"#/components/schemas/Slug"},"title":{"$ref":"#/components/schemas/LocalizedStringMap"},"summary":{"$ref":"#/components/schemas/LocalizedStringMap"},"latest_version":{"type":["string","null"]},"visibility":{"$ref":"#/components/schemas/PublicVisibility"},"access_tier":{"$ref":"#/components/schemas/PublicAccessTier"},"system_requirements":{"type":"array","items":{"type":"object"}},"install_requirements":{"type":"array","items":{"type":"object"}},"faq":{"type":"array","items":{"type":"object"}}}},"FileIntegrityHashes":{"type":"object","description":"Extensible hashes of the complete immutable stored file bytes. SHA-256 is canonical for new clients and is not interchangeable with an HTTP ETag or a digest of one range; SHA-1 and MD5 are compatibility-only when present.","required":["sha256"],"properties":{"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Canonical SHA-256 of the complete immutable stored file bytes. Verify it only after all downloaded ranges have been assembled and size_bytes matches."},"sha1":{"type":["string","null"],"pattern":"^[a-f0-9]{40}$","deprecated":true,"description":"Compatibility-only hash for legacy mod-manager ecosystems. Do not use as the canonical verifier."},"md5":{"type":["string","null"],"pattern":"^[a-f0-9]{32}$","deprecated":true,"description":"Compatibility-only checksum for legacy mod-manager ecosystems. Do not use as the canonical verifier."}},"additionalProperties":{"type":["string","null"],"description":"Future additive hash algorithms. Clients must ignore unknown algorithms unless explicitly supported."}},"PublicModArtifact":{"type":"object","required":["id","mod_id","version_id","file_kind","artifact_source","status"],"properties":{"id":{"$ref":"#/components/schemas/ArtifactId"},"mod_id":{"$ref":"#/components/schemas/ModId"},"version_id":{"$ref":"#/components/schemas/VersionId"},"file_kind":{"type":"string","description":"Role/category of this logical artifact within its mod version.","enum":["main","optional","old"]},"label":{"type":"string","description":"User-facing display name for the artifact. This is presentation metadata, not its identity."},"original_filename":{"type":["string","null"],"description":"Sanitized original filename retained only as a presentation hint; clients must use id as the durable identity."},"content_type":{"type":["string","null"],"description":"Stored media-type metadata for the artifact. Download security and validation do not rely on a client filename extension."},"game_version_key":{"type":["string","null"],"maxLength":120,"description":"Game-version compatibility key for this artifact when the game supports per-file platform targeting."},"loader_key":{"type":["string","null"],"maxLength":120,"description":"Loader/platform compatibility key for this artifact, such as fabric, forge, neoforge, quilt, or another game-specific loader."},"size_bytes":{"type":"integer","minimum":0,"description":"Complete stored-object size in bytes."},"sha256":{"type":["string","null"],"pattern":"^[a-f0-9]{64}$"},"hashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"artifact_source":{"type":"string","description":"Storage lineage only; provider bucket and object paths are never public identities.","enum":["r2_blob","legacy_external_url"]},"status":{"type":"string","description":"Logical artifact lifecycle status."},"scan_status":{"type":"string","description":"Actual AV scan-worker state for the current immutable blob, sourced only from an upload session matching both the logical file and final blob.","enum":["pending","queued","scanning","clean","quarantined","rejected","failed","skipped"]},"download_metadata":{"$ref":"#/components/schemas/ArtifactDownloadMetadata"}}},"ArtifactDownloadMetadata":{"type":"object","required":["resolve_endpoint","range_supported"],"additionalProperties":false,"properties":{"resolve_endpoint":{"type":"string","pattern":"^/v1/downloads/[^/]+/resolve$","description":"Provider-opaque control-plane endpoint that returns a short-lived artifact-bound download grant."},"range_supported":{"type":"boolean","description":"Whether resolved transports support resumable byte-range requests."}}},"PublicModArtifactDetails":{"description":"Complete managed artifact metadata emitted only when the current immutable blob has truthful integrity, AV-scan, and resolve-eligibility evidence.","unevaluatedProperties":false,"allOf":[{"$ref":"#/components/schemas/PublicModArtifact"},{"type":"object","required":["id","mod_id","version_id","file_kind","label","original_filename","content_type","game_version_key","loader_key","size_bytes","sha256","hashes","artifact_source","status","scan_status","download_metadata"],"properties":{"id":{"$ref":"#/components/schemas/ArtifactId"},"mod_id":{"$ref":"#/components/schemas/ModId"},"version_id":{"$ref":"#/components/schemas/VersionId"},"file_kind":{"type":"string","enum":["main","optional","old"]},"label":{"type":"string"},"original_filename":{"type":["string","null"]},"content_type":{"type":["string","null"]},"game_version_key":{"type":["string","null"],"maxLength":120},"loader_key":{"type":["string","null"],"maxLength":120},"size_bytes":{"type":"integer","minimum":1},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"hashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"artifact_source":{"const":"r2_blob"},"status":{"type":"string","enum":["ready","published"]},"scan_status":{"const":"clean"},"download_metadata":{"$ref":"#/components/schemas/ArtifactDownloadMetadata"}}}]},"PublicModDependency":{"type":"object","required":["id","mod_id","dependency_kind","relation"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"mod_id":{"$ref":"#/components/schemas/ModId"},"target_mod_id":{"oneOf":[{"$ref":"#/components/schemas/ModId"},{"type":"null"}]},"dependency_kind":{"type":"string","enum":["required_mod","external_requirement","incompatibility"]},"dependency_semantic":{"type":"string","enum":["required","optional","conflict","embedded"],"description":"Canonical machine-readable semantic. Legacy dependency_kind and relation remain available for older clients."},"label":{"type":["string","null"]},"url":{"type":["string","null"],"format":"uri"},"note":{"type":["string","null"]},"relation":{"type":"string"},"sort_order":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}},"PublicModPermissions":{"type":"object","required":["id","mod_id","owner_permissions","moderator_permissions","admin_permissions"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"mod_id":{"$ref":"#/components/schemas/ModId"},"uploader_is_original_author":{"type":"boolean"},"original_author_name":{"type":["string","null"]},"original_mod_url":{"type":["string","null"],"format":"uri"},"owner_permissions":{"type":"array","items":{"type":"string"}},"moderator_permissions":{"type":"array","items":{"type":"string"}},"admin_permissions":{"type":"array","items":{"type":"string"}}}},"PublicModLicense":{"type":"object","required":["id","mod_id","asset_usage_permissions"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"mod_id":{"$ref":"#/components/schemas/ModId"},"spdx_expression":{"type":["string","null"]},"custom_license":{"type":["object","null"]},"asset_usage_permissions":{"type":"object","additionalProperties":true},"homepage_url":{"type":["string","null"],"format":"uri"},"source_url":{"type":["string","null"],"format":"uri"}}},"ModListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicMod"}},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"ModResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/PublicMod"}}},"ModVersionListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicModVersion"}},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"UploadArchiveManifest":{"type":"object","additionalProperties":false,"properties":{"format":{"type":"string","enum":["zip","rar","7z"]},"entryCount":{"type":"integer","minimum":0,"maximum":1000000},"totalUncompressedBytes":{"type":"integer","minimum":0},"rootPaths":{"type":"array","maxItems":128,"items":{"type":"string","minLength":1,"maxLength":500}},"manifestSha256":{"type":["string","null"],"pattern":"^[a-fA-F0-9]{64}$"}}},"UploadBuildMetadata":{"type":"object","additionalProperties":false,"properties":{"source":{"type":"string","enum":["local","ci","tool"]},"commitSha":{"type":["string","null"],"pattern":"^[a-fA-F0-9]{7,64}$"},"tag":{"type":["string","null"],"minLength":1,"maxLength":160},"release":{"type":["string","null"],"minLength":1,"maxLength":160},"repository":{"type":["string","null"],"minLength":1,"maxLength":300},"workflowRunId":{"type":["string","null"],"minLength":1,"maxLength":160},"client":{"type":["string","null"],"minLength":1,"maxLength":120}}},"UploadRequestMetadata":{"type":"object","additionalProperties":false,"properties":{"archiveManifest":{"oneOf":[{"$ref":"#/components/schemas/UploadArchiveManifest"},{"type":"null"}]},"buildMetadata":{"oneOf":[{"$ref":"#/components/schemas/UploadBuildMetadata"},{"type":"null"}]}}},"ValidateUploadRequest":{"$ref":"#/components/schemas/CreateUploadRequest"},"UploadValidationResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","additionalProperties":false,"required":["valid","mode","fileId","originalFilename","sizeBytes","contentType","expectedSha256","metadata","strategy","constraints","checks"],"properties":{"valid":{"type":"boolean","const":true},"mode":{"type":"string","const":"validate_only"},"fileId":{"type":"integer","minimum":1},"originalFilename":{"type":"string","minLength":1,"maxLength":255},"sizeBytes":{"type":"integer","minimum":1},"contentType":{"type":"string","maxLength":120},"expectedSha256":{"type":["string","null"],"pattern":"^[a-f0-9]{64}$"},"metadata":{"$ref":"#/components/schemas/UploadRequestMetadata"},"strategy":{"type":"object","additionalProperties":false,"required":["kind","partSizeBytes","parallelism"],"properties":{"kind":{"type":"string","enum":["single","multipart"]},"partSizeBytes":{"type":["integer","null"],"minimum":1},"parallelism":{"type":["integer","null"],"minimum":1}}},"constraints":{"type":"object","additionalProperties":false,"required":["maxArchiveBytes","sha256Required"],"properties":{"maxArchiveBytes":{"type":"integer","minimum":1},"sha256Required":{"type":"boolean"}}},"checks":{"type":"array","minItems":5,"maxItems":5,"items":{"type":"string","enum":["authorization","ownership","archive_format","size_budget","checksum_policy"]}}}}}},"UploadContractLifecycleStatus":{"type":"string","description":"Canonical end-to-end creator automation state. The legacy status field remains additive compatibility metadata.","enum":["draft","uploading","uploaded","validating","scanning","quarantined","ready","published","failed","cancelled"]},"UploadContractErrorCode":{"type":"string","description":"Current stable machine-readable upload/version preflight and lifecycle error codes. Clients must still handle unknown future codes as generic API errors.","enum":["account_blocked","archive_dangerous_entry","archive_duplicate_path","archive_encrypted_entry","archive_path_traversal","archive_symlink_entry","archive_too_large","av_scan_rejected","duplicate_version","file_not_found","forbidden","idempotency_conflict","idempotency_in_progress","idempotency_key_required","idempotency_replay_unavailable","insufficient_scope","invalid_file_id","invalid_input","invalid_json","invalid_metadata","invalid_part_numbers","invalid_parts","invalid_request","invalid_sha256","invalid_size","invalid_staging_object","invalid_state_transition","missing_sha256","not_found","part_checksum_mismatch","part_size_mismatch","rate_limited","request_body_too_large","service_unavailable","session_expired","session_not_abortable","session_not_found","session_not_multipart","session_not_uploaded","session_not_uploading","sha256_mismatch","size_mismatch","unauthorized","unsupported_archive_extension","version_not_ready"],"x-moddingflow-tolerant-enum-additions":{"validated":true,"extensible":true,"values":["idempotency_replay_unavailable"],"validatedClients":["typescript-sdk","python-sdk","csharp-sdk"],"unknownValueBehavior":"preserve_as_generic_machine_code","evidence":"scripts/public-api-old-client-compat.mjs; tests/unit/scripts/public-api-old-client-compat.test.ts; tests/unit/lib/publicApi/apiKeyRoutes.server.test.ts; tests/unit/lib/publicApi/writeIdempotency.server.test.ts; tests/unit/scripts/public-api-openapi-lint.test.ts"}},"CreateUploadRequest":{"type":"object","required":["fileId","originalFilename","sizeBytes"],"additionalProperties":false,"properties":{"fileId":{"type":"integer","minimum":1,"description":"Legacy file row id returned by the version draft upload helper."},"originalFilename":{"type":"string","minLength":1,"maxLength":255},"sizeBytes":{"type":"integer","minimum":1},"contentType":{"type":["string","null"],"maxLength":120},"expectedHashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"expectedSha256":{"type":["string","null"],"pattern":"^[a-fA-F0-9]{64}$"},"archiveManifest":{"oneOf":[{"$ref":"#/components/schemas/UploadArchiveManifest"},{"type":"null"}]},"buildMetadata":{"oneOf":[{"$ref":"#/components/schemas/UploadBuildMetadata"},{"type":"null"}]}}},"UploadSessionLifecycleStatus":{"type":"string","description":"Stable upload-session lifecycle state. aborted is the public cancel state for sessions stopped by the client.","enum":["created","uploading","uploaded","processing","ready","failed","aborted","expired"]},"UploadSessionActionEndpoints":{"type":"object","required":["status","parts","complete","abort"],"properties":{"status":{"type":"string","description":"GET endpoint for the current upload-session status."},"parts":{"type":"string","description":"POST endpoint for refreshing multipart signed part URLs."},"complete":{"type":"string","description":"POST endpoint for committing uploaded bytes."},"abort":{"type":"string","description":"POST endpoint for canceling the upload-session."}},"additionalProperties":false},"UploadRetryPolicy":{"type":"object","required":["idempotencyKeyRequired","refreshSignedUrls","resumeStrategy","metadataOnlyChangesRequireReupload"],"properties":{"idempotencyKeyRequired":{"type":"boolean","const":true},"refreshSignedUrls":{"type":"string"},"resumeStrategy":{"type":"string"},"metadataOnlyChangesRequireReupload":{"type":"boolean","const":false}},"additionalProperties":false},"UploadResumePolicy":{"type":"object","required":["supported","strategy"],"properties":{"supported":{"type":"boolean"},"strategy":{"type":"string","enum":["single","multipart"]},"partSizeBytes":{"type":["integer","null"],"minimum":1},"parallelism":{"type":["integer","null"],"minimum":1}},"additionalProperties":false},"UploadCompletedPart":{"type":"object","required":["partNumber","etag","sizeBytes"],"properties":{"partNumber":{"type":"integer","minimum":1,"maximum":10000},"etag":{"type":"string","minLength":1,"maxLength":256,"description":"Provider-confirmed multipart ETag required when completing or resuming the upload."},"sizeBytes":{"type":"integer","minimum":0}},"additionalProperties":false},"UploadProgress":{"type":"object","required":["available","uploadedBytes","totalBytes","percent","totalParts","completedParts"],"properties":{"available":{"type":"boolean","description":"True only when uploaded bytes come from a successful multipart provider listing or recorded actual_size_bytes after completion. False means current provider progress is unknown; numeric progress fields are placeholders and must not be interpreted as zero transfer or lost accepted bytes."},"uploadedBytes":{"type":"integer","minimum":0},"totalBytes":{"type":"integer","minimum":1},"percent":{"type":"number","minimum":0,"maximum":100},"totalParts":{"type":"integer","minimum":1,"maximum":10000},"completedParts":{"type":"array","maxItems":10000,"items":{"$ref":"#/components/schemas/UploadCompletedPart"}}},"additionalProperties":false},"AsyncJobStatus":{"type":"string","description":"Stable status values for public API long-running jobs. queued and processing are non-terminal; succeeded, rejected, failed, and cancelled are terminal.","x-moddingflow-non-terminal-values":["queued","processing"],"x-moddingflow-terminal-values":["succeeded","rejected","failed","cancelled"],"enum":["queued","processing","succeeded","rejected","failed","cancelled"]},"AsyncJobResource":{"type":"object","required":["kind","id"],"properties":{"kind":{"type":"string","enum":["upload-session"]},"id":{"type":"string","minLength":1}},"additionalProperties":false},"AsyncJobProgress":{"type":"object","description":"Truthful coarse lifecycle completion. This is a single job unit, not an estimated scan percentage: non-terminal jobs return 0/1 and terminal jobs return 1/1, including unsuccessful terminal outcomes.","required":["completed","total","percent","unit"],"properties":{"completed":{"type":"integer","minimum":0,"maximum":1},"total":{"type":"integer","const":1},"percent":{"type":"integer","minimum":0,"maximum":100},"unit":{"type":"string","const":"job"}},"additionalProperties":false},"AsyncJobRetryPolicy":{"type":"object","required":["retryable","strategy","retryAfterSeconds"],"properties":{"retryable":{"type":"boolean"},"strategy":{"type":"string","enum":["worker_retry","manual_resume","not_retryable"]},"retryAfterSeconds":{"type":["integer","null"],"minimum":0,"description":"Suggested delay before the next status poll. Null when the job is terminal."},"attempt":{"type":"integer","minimum":0,"description":"Durable worker attempts already claimed."},"maxAttempts":{"type":"integer","minimum":1,"maximum":20},"nextAttemptAt":{"type":["string","null"],"format":"date-time","description":"Durable next worker eligibility time for a non-terminal job; null after terminal completion."}},"additionalProperties":false},"AsyncJob":{"type":"object","required":["id","kind","resource","status","statusEndpoint","retry"],"properties":{"id":{"type":"string","minLength":1},"kind":{"type":"string","enum":["upload_processing"]},"resource":{"$ref":"#/components/schemas/AsyncJobResource"},"status":{"$ref":"#/components/schemas/AsyncJobStatus"},"terminal":{"type":"boolean","description":"True only for succeeded, rejected, failed, or cancelled."},"statusEndpoint":{"type":"string","description":"Stable polling endpoint for this job."},"reason":{"type":["string","null"],"x-moddingflow-stable-machine-code":true,"description":"Stable allow-listed machine-readable terminal reason. Unknown provider prose is never forwarded and maps to av_scan_rejected or worker_failed; non-terminal jobs use null."},"progress":{"$ref":"#/components/schemas/AsyncJobProgress"},"retry":{"$ref":"#/components/schemas/AsyncJobRetryPolicy"},"createdAt":{"type":"string","format":"date-time","description":"Durable job creation time."},"updatedAt":{"type":"string","format":"date-time","description":"Last durable worker-state update time."},"startedAt":{"type":["string","null"],"format":"date-time","description":"Worker claim time; null while queued and never claimed."},"completedAt":{"type":["string","null"],"format":"date-time","description":"Terminal completion time; null while the job is non-terminal."}},"additionalProperties":false},"UploadSessionStatusResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["sessionId","supportCorrelationId","status","lifecycleStatus","strategy","fileId","expectedSizeBytes","expiresAt","expiresIn","job","metadata","reason","endpoints","retry","resume","progress"],"properties":{"sessionId":{"$ref":"#/components/schemas/UploadId"},"supportCorrelationId":{"$ref":"#/components/schemas/UploadSupportCorrelationId"},"status":{"$ref":"#/components/schemas/UploadSessionLifecycleStatus"},"lifecycleStatus":{"$ref":"#/components/schemas/UploadContractLifecycleStatus"},"strategy":{"type":"string","enum":["single","multipart"]},"fileId":{"type":"integer","minimum":1},"expectedSizeBytes":{"type":"integer","minimum":1},"actualSizeBytes":{"type":["integer","null"],"minimum":1},"expiresAt":{"type":"string","format":"date-time"},"expiresIn":{"type":"integer","minimum":0},"job":{"oneOf":[{"$ref":"#/components/schemas/AsyncJob"},{"type":"null"}]},"metadata":{"$ref":"#/components/schemas/UploadRequestMetadata"},"reason":{"type":["string","null"],"description":"Machine-readable lifecycle reason such as review_required, upload_cancelled, upload_expired, validation_failed, or a scan status."},"endpoints":{"$ref":"#/components/schemas/UploadSessionActionEndpoints"},"retry":{"$ref":"#/components/schemas/UploadRetryPolicy"},"resume":{"$ref":"#/components/schemas/UploadResumePolicy"},"progress":{"$ref":"#/components/schemas/UploadProgress"}},"additionalProperties":false}}},"UploadSessionResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"status":{"type":"integer","enum":[201]},"data":{"type":"object","required":["sessionId","supportCorrelationId","strategy","fileId","expiresIn"],"properties":{"sessionId":{"$ref":"#/components/schemas/UploadId"},"supportCorrelationId":{"$ref":"#/components/schemas/UploadSupportCorrelationId"},"strategy":{"type":"string","enum":["single","multipart"]},"fileId":{"type":"integer","minimum":1},"expiresIn":{"type":"integer","minimum":1},"expiresAt":{"type":"string","format":"date-time"},"status":{"$ref":"#/components/schemas/UploadSessionLifecycleStatus"},"method":{"type":"string","enum":["PUT"]},"uploadUrl":{"type":"string","format":"uri"},"urlExpiresIn":{"type":"integer","minimum":1,"description":"Expiry in seconds for the current single PUT signed URL; separate from upload-session expiry."},"headers":{"type":"object","description":"Headers signed into the current single PUT URL. Send every returned field exactly as supplied. HTTP field names are case-insensitive; the runtime returns if-none-match: * and the single PUT is invalid without that no-overwrite precondition. A conditional 412 is ambiguous-existing and must be followed by the same completion operation for authoritative server verification.","x-moddingflow-required-when-strategy":"single","x-moddingflow-required-header-names":["if-none-match"],"x-moddingflow-required-header-values":{"if-none-match":"*"},"example":{"if-none-match":"*"},"additionalProperties":{"type":"string"}},"uploadId":{"type":"string"},"partSizeBytes":{"type":"integer","minimum":1},"parallelism":{"type":"integer","minimum":1},"partsEndpoint":{"type":"string"},"completeEndpoint":{"type":"string"},"abortEndpoint":{"type":"string"},"statusEndpoint":{"type":"string"},"publicPartsEndpoint":{"type":"string"},"publicCompleteEndpoint":{"type":"string"},"publicAbortEndpoint":{"type":"string"},"publicStatusEndpoint":{"type":"string"},"retry":{"$ref":"#/components/schemas/UploadRetryPolicy"},"resume":{"$ref":"#/components/schemas/UploadResumePolicy"}},"additionalProperties":false}}},"DownloadResolveRequest":{"type":"object","properties":{"client":{"type":"string","enum":["website","mod_manager","automation","agent"]},"preferred_cdn":{"type":["string","null"],"maxLength":80,"description":"Optional provider-neutral route hint. Servers may ignore unknown values and clients must not treat provider names as stable contract identifiers."},"job_id":{"$ref":"#/components/schemas/Uuid","description":"Client-retained durable download job id returned by a previous success or 429. Omit to create a new logical job."},"progress":{"type":"object","required":["bytes_received"],"properties":{"bytes_received":{"type":"integer","minimum":0,"description":"Monotonic count of durable bytes already received for this job."},"etag":{"type":["string","null"],"maxLength":256,"description":"Opaque validator of the transport representation associated with the progress checkpoint."}},"additionalProperties":false}},"additionalProperties":false},"DownloadJob":{"type":"object","required":["id","grant_id","artifact_id","status","bytes_received","attempt_count","rate_limit_count","provider_effect_count"],"properties":{"id":{"$ref":"#/components/schemas/Uuid","description":"Actor-bound durable retry identity. Resend as job_id after 429 or transport interruption."},"grant_id":{"$ref":"#/components/schemas/Uuid","description":"Stable grant identity reserved once for this job; equals download_session_id when a signed grant is active."},"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"status":{"type":"string","enum":["pending","waiting_for_rate_limit","signing","grant_active","completed","cancelled","failed"]},"bytes_received":{"type":"integer","minimum":0},"etag":{"type":["string","null"],"maxLength":256},"attempt_count":{"type":"integer","minimum":0},"rate_limit_count":{"type":"integer","minimum":0},"provider_effect_count":{"type":"integer","minimum":0,"maximum":1,"description":"Remains zero across 429 responses and becomes one only when the stable grant is activated."},"next_attempt_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"DownloadGrant":{"type":"object","required":["id","artifact_id","expires_at","ttl_seconds","refresh_after_seconds","resolve_endpoint","fallback_endpoint","status"],"properties":{"id":{"$ref":"#/components/schemas/Uuid","description":"Opaque download-grant id. Mirrors DownloadResolveResponse.data.download_session_id."},"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"expires_at":{"type":"string","format":"date-time","description":"Absolute time when the grant and its signed URL artifacts stop being usable."},"ttl_seconds":{"type":"integer","minimum":1,"description":"Grant lifetime in seconds from the resolve response."},"refresh_after_seconds":{"type":"integer","minimum":1,"description":"Recommended refresh delay before expires_at."},"resolve_endpoint":{"type":"string","description":"Endpoint to call again for a fresh grant and signed URL."},"fallback_endpoint":{"type":"string","description":"Endpoint that accepts downloadSessionId to resolve a fallback signed URL."},"status":{"type":"string","enum":["active"]},"access":{"type":"object","description":"Artifact/user/client/scope/entitlement context. Treat unknown fields as additive policy detail.","additionalProperties":true}},"additionalProperties":false},"DownloadResolveResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["artifact_id","download_session_id","primary_url","head_url","expires_at","sha256","hashes","size_bytes","accept_ranges","conditional_headers","range_supported"],"properties":{"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"download_session_id":{"$ref":"#/components/schemas/Uuid"},"download_job":{"$ref":"#/components/schemas/DownloadJob"},"download_grant":{"$ref":"#/components/schemas/DownloadGrant"},"primary_url":{"type":"string","format":"uri"},"head_url":{"type":"string","format":"uri","description":"Short-lived HeadObject transport URL. Refresh through resolve; never persist it as file identity."},"primary":{"type":"object","required":["provider","url","expiresIn"],"properties":{"provider":{"type":"string","description":"Diagnostic transport provider name. Not a durable storage identity."},"url":{"type":"string","format":"uri"},"headUrl":{"type":"string","format":"uri"},"expiresIn":{"type":"integer","minimum":1}}},"fallback_url":{"type":["string","null"],"format":"uri"},"fallback_endpoint":{"type":"string"},"fallback":{"type":"object","properties":{"available":{"type":"boolean"},"endpoint":{"type":"string"},"legacy_endpoint":{"type":["string","null"]},"provider":{"type":"string","description":"Diagnostic fallback transport provider name. Not a durable storage identity."},"reason":{"type":"string"}}},"expires_at":{"type":"string","format":"date-time"},"expires_in":{"type":"integer","minimum":1},"refresh_after_seconds":{"type":"integer","minimum":1},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Legacy alias for hashes.sha256: SHA-256 of the complete immutable stored object bytes, not an individual HTTP range."},"hashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"size_bytes":{"type":"integer","minimum":1},"etag":{"type":"string","description":"Opaque transport representation validator for conditional requests and range resume. It is not a cryptographic content digest, artifact identity, or SHA-256 substitute."},"accept_ranges":{"type":"string","const":"bytes"},"conditional_headers":{"type":"array","items":{"type":"string","enum":["If-Match","If-None-Match","If-Modified-Since","If-Unmodified-Since"]},"minItems":4,"maxItems":4,"uniqueItems":true},"content_disposition":{"type":"string"},"range_supported":{"type":"boolean"},"chunk_size_hint_bytes":{"type":"integer","minimum":1},"verification":{"type":"object","required":["sha256","hashes","size_bytes"],"properties":{"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"hashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"etag":{"type":["string","null"],"description":"Opaque transport representation validator; never compare it with sha256 or hashes.sha256."},"size_bytes":{"type":"integer","minimum":1}}},"resume":{"type":"object","required":["range_supported","sha256","hashes","head_url","conditional_headers","chunk_size_hint_bytes","signed_url_expiry_refresh"],"properties":{"range_supported":{"type":"boolean"},"etag":{"type":["string","null"],"description":"Opaque validator used to keep resumed ranges on the same representation. It does not verify an individual range or the complete file."},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"hashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"head_url":{"type":"string","format":"uri"},"conditional_headers":{"type":"array","items":{"type":"string"},"minItems":4},"chunk_size_hint_bytes":{"type":"integer","minimum":1},"signed_url_expiry_refresh":{"type":"string"}}}}}}},"DownloadFallbackRequest":{"type":"object","required":["downloadSessionId"],"properties":{"downloadSessionId":{"$ref":"#/components/schemas/Uuid"},"cloudflareProbeFailed":{"type":"boolean"},"probe":{"type":["object","null"],"properties":{"ok":{"type":["boolean","null"]},"failed":{"type":["boolean","null"]},"status":{"type":["integer","null"]},"bytesReceived":{"type":["integer","null"],"minimum":0},"reason":{"type":["string","null"],"maxLength":80}},"additionalProperties":false}},"additionalProperties":false},"DownloadFallbackResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["artifact_id","download_session_id","fallback_url","provider","expires_at"],"properties":{"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"download_session_id":{"$ref":"#/components/schemas/Uuid"},"fallback_url":{"type":"string","format":"uri"},"url":{"type":"string","format":"uri"},"provider":{"type":"string","description":"Diagnostic fallback transport provider name. Not a durable storage identity."},"reason":{"type":"string","enum":["bunny_ru_geo","bunny_probe_failure"]},"expires_at":{"type":"string","format":"date-time"},"expires_in":{"type":"integer","minimum":1}}}}},"InstallPlanResolveRequest":{"type":"object","required":["game_slug","game_version"],"properties":{"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"artifact_ids":{"type":"array","items":{"$ref":"#/components/schemas/ArtifactId"},"maxItems":128},"game_slug":{"$ref":"#/components/schemas/Slug"},"game_version":{"type":"string","minLength":1,"maxLength":80},"loader":{"type":["string","null"]},"platform":{"type":["string","null"]},"release_channel":{"$ref":"#/components/schemas/ReleaseChannel","default":"stable"},"mod_id":{"$ref":"#/components/schemas/ModId"},"mod_ids":{"type":"array","items":{"$ref":"#/components/schemas/ModId"},"maxItems":128},"version_id":{"$ref":"#/components/schemas/VersionId"},"version_ids":{"type":"array","items":{"$ref":"#/components/schemas/VersionId"},"maxItems":128},"include_optional":{"type":"boolean","default":false}},"additionalProperties":false},"InstallPlanResolveResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["plan_id","game_slug","game_version","install_order","warnings"],"properties":{"plan_id":{"$ref":"#/components/schemas/Uuid"},"legacy_plan_id":{"$ref":"#/components/schemas/Uuid","description":"Compatibility alias for the pre-Stage-02 deterministic plan identifier. New clients use plan_id."},"game_slug":{"$ref":"#/components/schemas/Slug"},"game_version":{"type":"string"},"loader":{"type":["string","null"]},"platform":{"type":["string","null"]},"release_channel":{"$ref":"#/components/schemas/ReleaseChannel"},"dependency_constraints":{"type":"array","items":{"$ref":"#/components/schemas/InstallPlanDependencyConstraint"}},"conflicts":{"type":"array","items":{"$ref":"#/components/schemas/InstallPlanConflict"}},"required_dependencies":{"type":"array","items":{"type":"object","additionalProperties":true}},"optional_dependencies":{"type":"array","items":{"type":"object","additionalProperties":true}},"incompatibilities":{"type":"array","items":{"type":"object","additionalProperties":true}},"required_disk_size_bytes":{"type":"integer","minimum":0},"install_order":{"type":"array","items":{"$ref":"#/components/schemas/InstallPlanStep"}},"file_hashes":{"type":"array","items":{"type":"object","required":["artifact_id","sha256","hashes"],"properties":{"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"hashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"etag":{"type":["string","null"]}}}},"expected_archive_layout":{"type":"object","additionalProperties":true},"resume":{"type":"object","properties":{"range_supported":{"type":"boolean"},"sha256_verification":{"type":"boolean"},"etag_verification":{"type":"boolean"},"chunk_size_hint_bytes":{"type":"integer","minimum":1},"signed_url_refresh":{"type":"string"}}},"warnings":{"type":"array","items":{"type":"string"}}}}}},"InstallPlanStep":{"type":"object","required":["mod_id","version_id","artifact_id","required","sha256","hashes"],"properties":{"item_id":{"$ref":"#/components/schemas/Uuid","description":"Stable deterministic identity for this selected artifact and decision within the plan."},"step_index":{"type":"integer","minimum":1},"mod_id":{"$ref":"#/components/schemas/ModId"},"version_id":{"$ref":"#/components/schemas/VersionId"},"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"required":{"type":"boolean"},"selection_kind":{"type":"string","enum":["selected_artifact","selected_version","selected_mod","required_dependency","optional_dependency"]},"decision_reasons":{"type":"array","items":{"type":"string"}},"dependency_reason":{"type":["string","null"]},"download_resolve_endpoint":{"type":"string"},"file":{"type":"object","required":["kind","size_bytes"],"properties":{"kind":{"type":"string","enum":["main","optional","old"]},"label":{"type":["string","null"]},"filename":{"type":["string","null"]},"content_type":{"type":["string","null"]},"size_bytes":{"type":"integer","minimum":1}}},"file_hashes":{"type":"object","required":["sha256"],"properties":{"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"sha1":{"type":["string","null"],"pattern":"^[a-f0-9]{40}$","deprecated":true},"md5":{"type":["string","null"],"pattern":"^[a-f0-9]{32}$","deprecated":true},"etag":{"type":["string","null"]}},"additionalProperties":{"type":["string","null"]}},"hashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"expected_archive_layout":{"type":"object","additionalProperties":true}}},"InstallPlanDependencyConstraint":{"type":"object","required":["dependency_id","semantic","relation"],"properties":{"dependency_id":{"$ref":"#/components/schemas/Uuid"},"mod_id":{"$ref":"#/components/schemas/ModId"},"target_mod_id":{"oneOf":[{"$ref":"#/components/schemas/ModId"},{"type":"null"}]},"semantic":{"type":"string","enum":["required","optional","embedded"]},"relation":{"type":"string"},"reason":{"type":["string","null"]}}},"InstallPlanConflict":{"type":"object","required":["dependency_id","semantic","relation"],"properties":{"dependency_id":{"$ref":"#/components/schemas/Uuid"},"mod_id":{"$ref":"#/components/schemas/ModId"},"target_mod_id":{"oneOf":[{"$ref":"#/components/schemas/ModId"},{"type":"null"}]},"semantic":{"const":"conflict"},"relation":{"type":"string"},"reason":{"type":["string","null"]}}},"PublicApiScope":{"type":"string","enum":["openid","mods:read","mods:write","versions:write","versions:publish","versions:archive","files:upload","files:manage","files:download","install_plans:resolve","profile:read","webhooks:read","webhooks:write","private_data:read","teams:admin","agent:run","agent:tools:read","agent:tools:download","agent:tools:publish_draft","agent:tools:write_confirmed","agent:tools:write","admin:*"],"description":"Granular public API scope. admin:* is reserved for trusted internal clients only."},"OAuthClientKey":{"type":"string","minLength":3,"maxLength":81,"pattern":"^[a-z][a-z0-9_]{2,80}$","examples":["website","app_550e8400e29b41d4a716446655440000"],"description":"Opaque registered OAuth client identifier. First-party clients use stable lowercase registry keys and self-service applications use app_<lowercase UUID/hex>. This schema is intentionally open and extensible: treat values as opaque and do not branch on a fixed enum."},"ScopeString":{"type":"string","description":"Space-delimited public API scopes.","examples":["mods:read files:download install_plans:resolve"]},"DeviceAuthorizationRequest":{"type":"object","properties":{"client_id":{"$ref":"#/components/schemas/OAuthClientKey"},"client_secret":{"type":"string","minLength":1,"writeOnly":true,"description":"Confidential client secret for registered client_secret_post clients. Prefer HTTP Basic where supported."},"scope":{"$ref":"#/components/schemas/ScopeString"}},"additionalProperties":false},"DeviceAuthorizationResponse":{"type":"object","required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"],"properties":{"device_code":{"type":"string","minLength":16},"user_code":{"type":"string","minLength":6,"maxLength":16},"verification_uri":{"type":"string","format":"uri"},"verification_uri_complete":{"type":"string","format":"uri"},"expires_in":{"type":"integer","minimum":60},"interval":{"type":"integer","minimum":5}}},"OAuthTokenRequest":{"oneOf":[{"$ref":"#/components/schemas/AuthorizationCodeTokenRequest"},{"$ref":"#/components/schemas/RefreshTokenRequest"},{"$ref":"#/components/schemas/DeviceCodeTokenRequest"},{"$ref":"#/components/schemas/AgentTokenExchangeRequest"}]},"AuthorizationCodeTokenRequest":{"type":"object","required":["grant_type","code","redirect_uri","code_verifier"],"properties":{"grant_type":{"type":"string","const":"authorization_code"},"client_id":{"$ref":"#/components/schemas/OAuthClientKey"},"client_secret":{"type":"string","minLength":1,"writeOnly":true},"code":{"type":"string","minLength":16},"redirect_uri":{"type":"string","format":"uri"},"code_verifier":{"type":"string","minLength":43,"maxLength":128}},"additionalProperties":false},"RefreshTokenRequest":{"type":"object","required":["grant_type","refresh_token"],"properties":{"grant_type":{"type":"string","const":"refresh_token"},"client_id":{"$ref":"#/components/schemas/OAuthClientKey"},"client_secret":{"type":"string","minLength":1,"writeOnly":true},"refresh_token":{"type":"string","minLength":16},"scope":{"$ref":"#/components/schemas/ScopeString"}},"additionalProperties":false},"DeviceCodeTokenRequest":{"type":"object","required":["grant_type","device_code"],"properties":{"grant_type":{"type":"string","const":"urn:ietf:params:oauth:grant-type:device_code"},"client_id":{"$ref":"#/components/schemas/OAuthClientKey"},"client_secret":{"type":"string","minLength":1,"writeOnly":true},"device_code":{"type":"string","minLength":16}},"additionalProperties":false},"OAuthTokenResponse":{"type":"object","required":["access_token","token_type","expires_in","scope"],"properties":{"access_token":{"type":"string","minLength":16},"token_type":{"type":"string","const":"Bearer"},"expires_in":{"type":"integer","minimum":60,"maximum":3600},"refresh_token":{"type":"string","minLength":16},"scope":{"$ref":"#/components/schemas/ScopeString"},"id_token":{"type":"string","minLength":16},"issued_token_type":{"type":"string","enum":["urn:ietf:params:oauth:token-type:access_token"]}}},"OAuthRevocationRequest":{"type":"object","required":["token"],"properties":{"token":{"type":"string","minLength":16},"token_type_hint":{"type":"string","enum":["access_token","refresh_token","personal_access_token"]},"client_id":{"$ref":"#/components/schemas/OAuthClientKey"},"client_secret":{"type":"string","minLength":1,"writeOnly":true,"description":"Confidential client secret for registered client_secret_post clients. Prefer HTTP Basic where supported."}},"additionalProperties":false},"OAuthRevocationResponse":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean","const":true},"revoked":{"type":"boolean","description":"True when this call changed state; false when the token was already inactive."}}},"OAuthIntrospectionRequest":{"type":"object","required":["token"],"properties":{"token":{"type":"string","minLength":16},"token_type_hint":{"type":"string","enum":["access_token","refresh_token","personal_access_token"]}},"additionalProperties":false},"OAuthIntrospectionResponse":{"type":"object","required":["active"],"properties":{"active":{"type":"boolean"},"scope":{"$ref":"#/components/schemas/ScopeString"},"client_id":{"$ref":"#/components/schemas/OAuthClientKey"},"sub":{"type":"string"},"aud":{"type":"string"},"iss":{"type":"string","format":"uri"},"exp":{"type":"integer"},"iat":{"type":"integer"},"token_type":{"type":"string"}}},"OAuthAuthorizationServerMetadata":{"type":"object","required":["issuer","authorization_endpoint","device_authorization_endpoint","token_endpoint","revocation_endpoint","introspection_endpoint","response_types_supported","grant_types_supported","token_endpoint_auth_methods_supported","code_challenge_methods_supported","scopes_supported"],"properties":{"issuer":{"type":"string","const":"https://api.moddingflow.com"},"authorization_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/authorize"},"device_authorization_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/device_authorization"},"token_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/token"},"revocation_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/revoke"},"introspection_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/introspect"},"response_types_supported":{"type":"array","items":{"type":"string"},"examples":[["code"]]},"grant_types_supported":{"type":"array","items":{"type":"string"},"examples":[["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:token-exchange"]]},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"},"examples":[["S256"]]},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"},"examples":[["none","client_secret_basic","client_secret_post"]]},"scopes_supported":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}}},"additionalProperties":false},"OidcDiscoveryResponse":{"type":"object","required":["issuer","authorization_endpoint","device_authorization_endpoint","token_endpoint","revocation_endpoint","introspection_endpoint","jwks_uri","response_types_supported","response_modes_supported","grant_types_supported","token_endpoint_auth_methods_supported","code_challenge_methods_supported","subject_types_supported","id_token_signing_alg_values_supported","claims_supported","scopes_supported"],"properties":{"issuer":{"type":"string","const":"https://api.moddingflow.com"},"authorization_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/authorize"},"device_authorization_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/device_authorization"},"token_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/token"},"revocation_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/revoke"},"introspection_endpoint":{"type":"string","const":"https://api.moddingflow.com/oauth/introspect"},"jwks_uri":{"type":"string","const":"https://api.moddingflow.com/.well-known/jwks.json"},"response_types_supported":{"type":"array","items":{"type":"string"},"examples":[["code"]]},"response_modes_supported":{"type":"array","items":{"type":"string"},"examples":[["query"]]},"grant_types_supported":{"type":"array","items":{"type":"string"},"examples":[["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:token-exchange"]]},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"},"examples":[["S256"]]},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"},"examples":[["none","client_secret_basic","client_secret_post"]]},"subject_types_supported":{"type":"array","items":{"type":"string"},"examples":[["public"]]},"id_token_signing_alg_values_supported":{"type":"array","items":{"type":"string"},"examples":[["RS256"]]},"claims_supported":{"type":"array","items":{"type":"string"},"examples":[["iss","sub","aud","exp","iat","nonce"]]},"scopes_supported":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}}},"additionalProperties":false},"JsonWebKey":{"type":"object","required":["kty","kid","use","alg","n","e"],"properties":{"kty":{"type":"string","const":"RSA"},"kid":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._-]+$"},"use":{"type":"string","const":"sig"},"alg":{"type":"string","const":"RS256"},"n":{"type":"string","minLength":128,"description":"Base64url-encoded RSA modulus."},"e":{"type":"string","minLength":1,"description":"Base64url-encoded RSA public exponent."}},"additionalProperties":false},"JsonWebKeySet":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/JsonWebKey"}}},"additionalProperties":false},"ApiKeyResourceGrant":{"type":"object","required":["resource_kind","resource_id"],"properties":{"resource_kind":{"type":"string","enum":["mod","project"],"description":"project is currently an alias for the canonical mod publishing project."},"resource_id":{"$ref":"#/components/schemas/Uuid"}},"additionalProperties":false},"CreateApiKeyRequest":{"type":"object","required":["name","scopes"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120},"scopes":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/PublicApiScope"},"description":"admin:* is never valid for an individual API key."},"resources":{"type":"array","maxItems":100,"uniqueItems":true,"items":{"$ref":"#/components/schemas/ApiKeyResourceGrant"},"default":[]},"expires_at":{"type":"string","format":"date-time","description":"Defaults to 90 days and cannot exceed 366 days."}},"additionalProperties":false},"PersonalAccessTokenMetadata":{"type":"object","required":["id","name","fingerprint","display_prefix","scopes","resources","resource_scope_mode","created_at"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"name":{"type":"string"},"display_prefix":{"type":"string","pattern":"^mfp_[A-Za-z0-9]{6,16}$"},"fingerprint":{"type":"string","pattern":"^mfp_[A-Za-z0-9]{6,16}$","description":"Safe non-secret prefix used in UI and audit logs."},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}},"resources":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyResourceGrant"}},"resource_scope_mode":{"type":"string","enum":["all","restricted"]},"last_used_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"},"revoked_reason":{"type":["string","null"]},"rotation_generation":{"type":"integer","minimum":0},"rotated_from_id":{"oneOf":[{"$ref":"#/components/schemas/Uuid"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time"}}},"ApiKeyOneTimeSecretResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"allOf":[{"$ref":"#/components/schemas/PersonalAccessTokenMetadata"},{"type":"object","required":["secret","secret_reveal"],"properties":{"secret":{"type":"string","pattern":"^mfp_[A-Za-z0-9_-]{32,}$","readOnly":true,"description":"Raw API-key secret returned once and never stored or returned again."},"secret_reveal":{"const":"one_time"}}}]}}},"ApiKeyListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/PersonalAccessTokenMetadata"}}}}}},"ApiKeyCapabilitiesResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"type":"object","required":["scopes","projects","roles"],"properties":{"scopes":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}},"projects":{"type":"array","items":{"type":"object","required":["mod_id","project_id","role","scopes"],"properties":{"mod_id":{"$ref":"#/components/schemas/Uuid"},"project_id":{"$ref":"#/components/schemas/Uuid"},"role":{"$ref":"#/components/schemas/TeamPublishingRole"},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}}}}},"roles":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}}}}}}},"ApiKeyRevocationResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"type":"object","required":["id","fingerprint","revoked","changed"],"properties":{"id":{"$ref":"#/components/schemas/Uuid"},"fingerprint":{"type":"string"},"revoked":{"const":true},"changed":{"type":"boolean"}}}}},"TeamPublishingRole":{"type":"string","enum":["owner","maintainer","publisher","reviewer"]},"SetTeamMemberRequest":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["maintainer","publisher","reviewer"]}},"additionalProperties":false},"TeamMember":{"type":"object","required":["user_id","role","capabilities"],"properties":{"user_id":{"$ref":"#/components/schemas/Uuid"},"role":{"$ref":"#/components/schemas/TeamPublishingRole"},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"TeamMemberListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"type":"object","required":["mod_id","project_id","members"],"properties":{"mod_id":{"$ref":"#/components/schemas/Uuid"},"project_id":{"$ref":"#/components/schemas/Uuid"},"members":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}}}}}},"TeamMemberResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"allOf":[{"$ref":"#/components/schemas/TeamMember"},{"type":"object","required":["mod_id","project_id"],"properties":{"mod_id":{"$ref":"#/components/schemas/Uuid"},"project_id":{"$ref":"#/components/schemas/Uuid"}}}]}}},"TeamMemberRemovalResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"type":"object","required":["mod_id","project_id","user_id","removed","changed"],"properties":{"mod_id":{"$ref":"#/components/schemas/Uuid"},"project_id":{"$ref":"#/components/schemas/Uuid"},"user_id":{"$ref":"#/components/schemas/Uuid"},"removed":{"const":true},"changed":{"type":"boolean"}}}}},"PublicCatalogCategory":{"type":"object","required":["slug","label"],"properties":{"slug":{"$ref":"#/components/schemas/Slug"},"label":{"$ref":"#/components/schemas/LocalizedStringMap"}}},"PublicCatalogFacet":{"type":"object","required":["facet","value","label"],"properties":{"facet":{"type":"string","minLength":1,"maxLength":80},"value":{"type":"string","minLength":1,"maxLength":120},"label":{"$ref":"#/components/schemas/LocalizedStringMap"},"game_id":{"$ref":"#/components/schemas/Uuid"},"game_slug":{"$ref":"#/components/schemas/Slug"},"count":{"type":"integer","minimum":0}}},"PublicChangelogEntry":{"type":"object","properties":{"version":{"type":["string","null"]},"body_md":{"type":["string","null"]},"released_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":true},"GameListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicGame"}}}}}},"GameResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/PublicGame"}}},"ModVersionResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/PublicModVersion"}}},"ModDependencyListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicModDependency"}}}}}},"CatalogFacetListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicCatalogFacet"}},"label_language":{"type":"string"}}}}},"CatalogSearchResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicMod"}},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}}}}},"PublishState":{"type":"string","enum":["draft","uploaded","quarantined","scanning","rejected","approved","published","archived"]},"ReleaseChannel":{"type":"string","enum":["stable","beta","alpha","experimental"]},"UploadPartsRequest":{"type":"object","required":["partNumbers"],"additionalProperties":false,"properties":{"partNumbers":{"type":"array","minItems":1,"maxItems":128,"items":{"type":"integer","minimum":1,"maximum":10000}}}},"UploadPartsResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["parts"],"properties":{"parts":{"type":"array","items":{"type":"object","required":["partNumber","url","sizeBytes","expiresIn"],"properties":{"partNumber":{"type":"integer","minimum":1,"maximum":10000},"url":{"type":"string","format":"uri"},"sizeBytes":{"type":"integer","minimum":1},"expiresIn":{"type":"integer","minimum":1}}}}}}}},"CompleteUploadRequest":{"type":"object","additionalProperties":false,"properties":{"expectedHashes":{"$ref":"#/components/schemas/FileIntegrityHashes"},"expectedSha256":{"type":["string","null"],"pattern":"^[a-fA-F0-9]{64}$"},"parts":{"type":"array","maxItems":10000,"items":{"type":"object","required":["etag","partNumber"],"properties":{"etag":{"type":"string","minLength":1,"maxLength":256},"partNumber":{"type":"integer","minimum":1,"maximum":10000}},"additionalProperties":false}}}},"CompleteUploadResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["actualSizeBytes","contentType","etag","supportCorrelationId","status"],"properties":{"actualSizeBytes":{"type":"integer","minimum":1},"blobId":{"type":["string","null"],"format":"uuid"},"contentType":{"type":["string","null"]},"deduped":{"type":"boolean"},"etag":{"type":["string","null"]},"job":{"$ref":"#/components/schemas/AsyncJob"},"sessionId":{"$ref":"#/components/schemas/UploadId"},"supportCorrelationId":{"$ref":"#/components/schemas/UploadSupportCorrelationId"},"status":{"type":"string","enum":["processing","ready"]}}}}},"AbortUploadResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["status","lifecycleStatus","supportCorrelationId"],"properties":{"status":{"type":"string","enum":["aborted"]},"lifecycleStatus":{"type":"string","const":"cancelled"},"supportCorrelationId":{"$ref":"#/components/schemas/UploadSupportCorrelationId"}}}}},"CreateModRequest":{"type":"object","required":["game_slug","language_code","slug","title","summary","description_md","category_slugs"],"additionalProperties":false,"properties":{"game_slug":{"$ref":"#/components/schemas/Slug"},"language_code":{"type":"string","pattern":"^[a-z]{2}$"},"slug":{"$ref":"#/components/schemas/Slug"},"title":{"type":"string","minLength":1,"maxLength":160},"summary":{"type":"string","minLength":1,"maxLength":300},"description_md":{"type":"string","minLength":1,"maxLength":120000},"category_slugs":{"type":"array","minItems":1,"maxItems":12,"items":{"$ref":"#/components/schemas/Slug"}},"current_version":{"type":["string","null"],"maxLength":80},"homepage_url":{"type":["string","null"],"format":"uri","maxLength":500},"source_url":{"type":["string","null"],"format":"uri","maxLength":500},"is_nsfw":{"type":"boolean"}}},"UpdateModRequest":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"game_slug":{"$ref":"#/components/schemas/Slug"},"language_code":{"type":"string","pattern":"^[a-z]{2}$"},"slug":{"$ref":"#/components/schemas/Slug"},"title":{"type":"string","minLength":1,"maxLength":160},"summary":{"type":"string","minLength":1,"maxLength":300},"description_md":{"type":"string","minLength":1,"maxLength":120000},"category_slugs":{"type":"array","minItems":1,"maxItems":12,"items":{"$ref":"#/components/schemas/Slug"}},"current_version":{"type":["string","null"],"maxLength":80},"homepage_url":{"type":["string","null"],"format":"uri","maxLength":500},"source_url":{"type":["string","null"],"format":"uri","maxLength":500},"is_nsfw":{"type":"boolean"}}},"VersionBuildMetadata":{"type":"object","additionalProperties":false,"properties":{"source":{"type":"string","enum":["local","ci","tool"]},"commit_sha":{"type":["string","null"],"pattern":"^[a-fA-F0-9]{7,64}$"},"tag":{"type":["string","null"],"minLength":1,"maxLength":160},"release":{"type":["string","null"],"minLength":1,"maxLength":160},"repository":{"type":["string","null"],"minLength":1,"maxLength":300},"workflow_run_id":{"type":["string","null"],"minLength":1,"maxLength":160},"client":{"type":["string","null"],"minLength":1,"maxLength":120}}},"VersionChangelogInput":{"oneOf":[{"type":"string","minLength":1,"maxLength":120000},{"type":"object","additionalProperties":false,"properties":{"body":{"type":"string","minLength":1,"maxLength":120000},"body_md":{"type":"string","minLength":1,"maxLength":120000},"released_at":{"type":["string","null"],"format":"date-time"}},"anyOf":[{"required":["body"]},{"required":["body_md"]}]}]},"VersionDependencyInput":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"kind":{"type":"string","enum":["required_mod","external_requirement","incompatibility"]},"target_mod_id":{"oneOf":[{"$ref":"#/components/schemas/ModId"},{"type":"null"}]},"label":{"type":["string","null"],"minLength":1,"maxLength":200},"url":{"type":["string","null"],"format":"uri","maxLength":500},"note":{"type":["string","null"],"maxLength":2000}},"anyOf":[{"required":["target_mod_id"]},{"required":["label"]},{"required":["url"]}]},"VersionRequirementInput":{"type":"object","additionalProperties":false,"required":["kind","label"],"properties":{"kind":{"type":"string","enum":["skse","skse64","dlc","address_library","tool","other"]},"label":{"type":"string","minLength":1,"maxLength":200},"detail":{"type":["string","null"],"maxLength":2000},"url":{"type":["string","null"],"format":"uri","maxLength":500}}},"VersionRequestMetadata":{"type":"object","additionalProperties":false,"properties":{"version":{"type":"string","minLength":1,"maxLength":80},"label":{"type":"string","minLength":1,"maxLength":160},"release_channel":{"$ref":"#/components/schemas/ReleaseChannel"},"changelog":{"oneOf":[{"$ref":"#/components/schemas/VersionChangelogInput"},{"type":"null"}]},"file_category":{"type":"string","enum":["main","optional","old"]},"game_slug":{"$ref":"#/components/schemas/Slug"},"game_versions":{"type":"array","maxItems":32,"items":{"type":"string","minLength":1,"maxLength":120}},"loader_platforms":{"type":"array","maxItems":8,"items":{"type":"string","enum":["fabric","forge","neoforge","quilt"]}},"dependencies":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/VersionDependencyInput"}},"requirements":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/VersionRequirementInput"}},"build_metadata":{"oneOf":[{"$ref":"#/components/schemas/VersionBuildMetadata"},{"type":"null"}]},"publish_state":{"type":"string","enum":["draft","uploaded","quarantined","scanning","rejected","approved","published","archived"]},"publish_state_changed_at":{"type":"string","format":"date-time"}}},"CreateModVersionRequest":{"type":"object","required":["version"],"additionalProperties":false,"properties":{"version":{"type":"string","minLength":1,"maxLength":80},"label":{"type":"string","minLength":1,"maxLength":160},"file_kind":{"type":"string","enum":["main","optional","old"]},"file_category":{"type":"string","description":"Canonical alias for file_kind. If both are sent they must match.","enum":["main","optional","old"]},"release_channel":{"$ref":"#/components/schemas/ReleaseChannel"},"game_version_key":{"type":["string","null"],"maxLength":120},"loader_key":{"type":["string","null"],"maxLength":120},"changelog":{"oneOf":[{"$ref":"#/components/schemas/VersionChangelogInput"},{"type":"null"}]},"game_slug":{"$ref":"#/components/schemas/Slug"},"game_versions":{"type":"array","minItems":1,"maxItems":32,"items":{"type":"string","minLength":1,"maxLength":120}},"loader_platforms":{"type":"array","minItems":1,"maxItems":8,"items":{"type":"string","enum":["fabric","forge","neoforge","quilt"]}},"dependencies":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/VersionDependencyInput"}},"requirements":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/VersionRequirementInput"}},"build_metadata":{"oneOf":[{"$ref":"#/components/schemas/VersionBuildMetadata"},{"type":"null"}]}}},"UpdateModVersionRequest":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"version":{"type":"string","minLength":1,"maxLength":80},"label":{"type":"string","minLength":1,"maxLength":160},"file_kind":{"type":"string","enum":["main","optional","old"]},"file_category":{"type":"string","description":"Canonical alias for file_kind. If both are sent they must match.","enum":["main","optional","old"]},"release_channel":{"$ref":"#/components/schemas/ReleaseChannel"},"game_version_key":{"type":["string","null"],"maxLength":120},"loader_key":{"type":["string","null"],"maxLength":120},"changelog":{"oneOf":[{"$ref":"#/components/schemas/VersionChangelogInput"},{"type":"null"}]},"game_slug":{"$ref":"#/components/schemas/Slug"},"game_versions":{"type":"array","minItems":1,"maxItems":32,"items":{"type":"string","minLength":1,"maxLength":120}},"loader_platforms":{"type":"array","minItems":1,"maxItems":8,"items":{"type":"string","enum":["fabric","forge","neoforge","quilt"]}},"dependencies":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/VersionDependencyInput"}},"requirements":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/VersionRequirementInput"}},"build_metadata":{"oneOf":[{"$ref":"#/components/schemas/VersionBuildMetadata"},{"type":"null"}]}}},"PublicApiModWriteResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"status":{"type":"integer"},"data":{"type":"object","required":["id","state"],"properties":{"id":{"$ref":"#/components/schemas/ModId"},"source_topic_id":{"type":"integer","minimum":1},"state":{"$ref":"#/components/schemas/PublishState"},"slug":{"$ref":"#/components/schemas/Slug"}}}}},"PublicApiVersionWriteResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"status":{"type":"integer"},"data":{"type":"object","required":["id","mod_id","state"],"properties":{"id":{"$ref":"#/components/schemas/VersionId"},"mod_id":{"$ref":"#/components/schemas/ModId"},"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"source_file_id":{"type":"integer","minimum":1},"state":{"$ref":"#/components/schemas/PublishState"},"request_metadata":{"$ref":"#/components/schemas/VersionRequestMetadata"},"idempotent":{"type":"boolean"},"upload":{"type":"object","properties":{"file_id":{"type":"integer","minimum":1},"create_url":{"type":"string"}}}}}}},"AgentToolName":{"type":"string","enum":["mods.search","mods.get","install_plans.resolve","downloads.resolve","versions.compare","publisher.draft","publisher.validate"]},"AgentToolDefinition":{"type":"object","required":["name","title","mode","requiredScopes","dryRunDefault","confirmationRequiredFor","description"],"properties":{"name":{"$ref":"#/components/schemas/AgentToolName"},"title":{"type":"string"},"mode":{"type":"string","enum":["read","download","write"]},"requiredScopes":{"type":"array","items":{"$ref":"#/components/schemas/PublicApiScope"}},"dryRunDefault":{"type":"boolean"},"confirmationRequiredFor":{"type":"array","items":{"type":"string"}},"description":{"type":"string"},"schemaVersion":{"type":"integer","const":1,"description":"Version of this tool input/output contract."}}},"AgentGatewayPolicy":{"type":"object","required":["arbitrary_url_fetch","download_actions","private_data_exfiltration","redaction_in_logs"],"properties":{"arbitrary_url_fetch":{"type":"boolean","const":false},"download_actions":{"type":"string"},"private_data_exfiltration":{"type":"boolean","const":false},"redaction_in_logs":{"type":"boolean","const":true}}},"AgentToolRegistryResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["audience","dry_run_default","mcp_facade","policy","tools"],"properties":{"audience":{"type":"string","const":"agent-gateway"},"dry_run_default":{"type":"boolean","const":true},"mcp_facade":{"type":"string"},"policy":{"$ref":"#/components/schemas/AgentGatewayPolicy"},"schema_version":{"type":"integer","const":1},"supported_schema_versions":{"type":"array","items":{"type":"integer","enum":[1]},"minItems":1,"maxItems":1,"uniqueItems":true},"tools":{"type":"array","items":{"$ref":"#/components/schemas/AgentToolDefinition"}}}}}},"AgentToolRunRequest":{"type":"object","required":["tool"],"additionalProperties":false,"properties":{"tool":{"$ref":"#/components/schemas/AgentToolName"},"input":{"type":"object","additionalProperties":true},"dry_run":{"type":"boolean","default":true},"schema_version":{"type":"integer","minimum":1,"default":1,"description":"Requested Agent Gateway tool schema version. Omitted values default to version 1 for compatibility with existing clients."},"confirmation_token":{"type":"string","minLength":16,"description":"Required for confirmed write actions such as publish, delete/archive, permission changes, or paid/restricted access changes."}}},"AgentToolRunResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["tool","dry_run","explanation","data"],"properties":{"tool":{"$ref":"#/components/schemas/AgentToolName"},"dry_run":{"type":"boolean"},"explanation":{"type":"string"},"schema_version":{"type":"integer","const":1},"data":{"type":"object","additionalProperties":true}}}}},"AgentTokenExchangeRequest":{"type":"object","required":["grant_type","client_id","subject_token","subject_token_type","audience"],"properties":{"grant_type":{"type":"string","const":"urn:ietf:params:oauth:grant-type:token-exchange"},"client_id":{"type":"string","const":"embedded_ai_agent"},"client_secret":{"type":"string","minLength":16},"subject_token":{"type":"string","minLength":16},"subject_token_type":{"type":"string","const":"urn:ietf:params:oauth:token-type:access_token"},"requested_token_type":{"type":"string","const":"urn:ietf:params:oauth:token-type:access_token"},"audience":{"type":"string","const":"agent-gateway"},"scope":{"$ref":"#/components/schemas/ScopeString"}}},"WebhookSubscriptionId":{"type":"string","format":"uuid","description":"Opaque public webhook subscription id."},"WebhookDeliveryId":{"type":"string","format":"uuid","description":"Opaque public webhook delivery id. Receivers persist this id for idempotency."},"WebhookEventKind":{"type":"string","enum":["mod.updated","version.published","file.ready","file.failed","upload.completed","scan.completed","token.changed","app.changed"],"description":"Stable public webhook event name. New values are additive and require tolerant receivers."},"WebhookSubscriptionStatus":{"type":"string","enum":["active","disabled"]},"WebhookDeliveryStatus":{"type":"string","enum":["queued","delivering","delivered","failed","exhausted","cancelled"]},"WebhookSigning":{"type":"object","required":["algorithm","header","timestamp_header","delivery_id_header","payload_format"],"properties":{"algorithm":{"type":"string","const":"hmac-sha256"},"header":{"type":"string","const":"X-Moddingflow-Signature"},"timestamp_header":{"type":"string","const":"X-Moddingflow-Timestamp"},"delivery_id_header":{"type":"string","const":"X-Moddingflow-Delivery-Id"},"payload_format":{"type":"string","description":"Sign `${timestamp}.${raw_body}` and verify the v1 base64url HMAC-SHA256 signature."},"tolerance_seconds":{"type":"integer","minimum":1,"default":300}},"examples":[{"algorithm":"hmac-sha256","header":"X-Moddingflow-Signature","timestamp_header":"X-Moddingflow-Timestamp","delivery_id_header":"X-Moddingflow-Delivery-Id","payload_format":"Sign `${timestamp}.${raw_body}` and verify the v1 base64url HMAC-SHA256 signature.","tolerance_seconds":300}],"additionalProperties":false},"CreateWebhookSubscriptionRequest":{"type":"object","required":["target_url","event_kinds"],"properties":{"name":{"type":["string","null"],"maxLength":120},"target_url":{"type":"string","format":"uri","maxLength":500,"description":"HTTPS endpoint that receives signed webhook POST requests."},"event_kinds":{"type":"array","minItems":1,"maxItems":32,"items":{"$ref":"#/components/schemas/WebhookEventKind"}},"metadata":{"type":"object","additionalProperties":true}},"additionalProperties":false},"WebhookSubscription":{"type":"object","required":["id","target_url","event_kinds","status","signing","signing_secret_version","created_at","updated_at"],"properties":{"id":{"$ref":"#/components/schemas/WebhookSubscriptionId"},"target_url":{"type":"string","format":"uri"},"name":{"type":["string","null"]},"event_kinds":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventKind"}},"status":{"$ref":"#/components/schemas/WebhookSubscriptionStatus"},"secret_display_prefix":{"type":["string","null"],"description":"Non-secret prefix shown for rotation/debugging."},"signing":{"$ref":"#/components/schemas/WebhookSigning"},"signing_secret_version":{"type":["integer","null"],"minimum":1,"description":"Current Vault-backed signing secret version. Null means a fail-closed legacy subscription that cannot deliver."},"failure_count":{"type":"integer","minimum":0},"disabled_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"WebhookSubscriptionListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}},"additionalProperties":false}}},"WebhookSubscriptionResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["subscription","signing_secret","signing_secret_version"],"properties":{"subscription":{"$ref":"#/components/schemas/WebhookSubscription"},"signing_secret":{"type":"string","pattern":"^whsec_[A-Za-z0-9_-]{32,}$","description":"One-time raw signing secret returned only at creation or rotation."},"signing_secret_version":{"type":"integer","minimum":1}},"additionalProperties":false}}},"WebhookSecretRotationRequest":{"type":"object","properties":{"overlap_seconds":{"type":"integer","minimum":300,"maximum":86400,"default":3600}},"additionalProperties":false},"WebhookSecretRotationResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["subscription_id","signing_secret","signing_secret_version","secret_display_prefix","overlap_expires_at"],"properties":{"subscription_id":{"$ref":"#/components/schemas/WebhookSubscriptionId"},"signing_secret":{"type":"string","pattern":"^whsec_[A-Za-z0-9_-]{32,}$"},"signing_secret_version":{"type":"integer","minimum":2},"secret_display_prefix":{"type":"string"},"overlap_expires_at":{"type":"string","format":"date-time"}},"additionalProperties":false}},"additionalProperties":false},"WebhookEventSubject":{"type":"object","description":"Opaque resource identifiers for the event subject. Mutable slugs are never required.","properties":{"user_id":{"type":"string","format":"uuid"},"mod_id":{"$ref":"#/components/schemas/ModId"},"version_id":{"$ref":"#/components/schemas/VersionId"},"upload_session_id":{"$ref":"#/components/schemas/UploadId"},"artifact_id":{"$ref":"#/components/schemas/ArtifactId"},"job_id":{"type":"string","format":"uuid"}},"additionalProperties":false},"WebhookDeliveryPayload":{"type":"object","required":["id","delivery_id","kind","created_at","spec_version","event_id","type","version","occurred_at","subject","data"],"properties":{"id":{"$ref":"#/components/schemas/WebhookDeliveryId"},"delivery_id":{"$ref":"#/components/schemas/WebhookDeliveryId"},"spec_version":{"type":"string","const":"1.0"},"event_id":{"type":"string","format":"uuid","description":"Stable event identity preserved across linked manual redeliveries and used for business-effect deduplication."},"kind":{"$ref":"#/components/schemas/WebhookEventKind","deprecated":true,"description":"Compatibility alias for type."},"type":{"$ref":"#/components/schemas/WebhookEventKind"},"version":{"type":"integer","minimum":1},"created_at":{"type":"string","format":"date-time","deprecated":true,"description":"Compatibility alias for occurred_at."},"occurred_at":{"type":"string","format":"date-time"},"subject":{"$ref":"#/components/schemas/WebhookEventSubject"},"data":{"type":"object","additionalProperties":true}},"additionalProperties":true},"WebhookDelivery":{"type":"object","required":["id","delivery_id","subscription_id","event_kind","event_version","delivery_status","attempt_count","created_at","payload"],"properties":{"id":{"$ref":"#/components/schemas/WebhookDeliveryId"},"delivery_id":{"$ref":"#/components/schemas/WebhookDeliveryId"},"subscription_id":{"$ref":"#/components/schemas/WebhookSubscriptionId"},"event_kind":{"$ref":"#/components/schemas/WebhookEventKind"},"event_version":{"type":"integer","minimum":1},"delivery_status":{"$ref":"#/components/schemas/WebhookDeliveryStatus"},"attempt_count":{"type":"integer","minimum":0},"next_attempt_at":{"type":["string","null"],"format":"date-time"},"last_attempt_at":{"type":["string","null"],"format":"date-time"},"delivered_at":{"type":["string","null"],"format":"date-time"},"last_response_status":{"type":["integer","null"],"minimum":100,"maximum":599},"last_response_latency_ms":{"type":["integer","null"],"minimum":0,"maximum":600000},"last_response_snippet":{"type":["string","null"],"maxLength":1024,"description":"Bounded and secret-redacted receiver response snippet."},"last_error_code":{"type":["string","null"]},"payload":{"$ref":"#/components/schemas/WebhookDeliveryPayload"},"redelivery_of_delivery_id":{"oneOf":[{"$ref":"#/components/schemas/WebhookDeliveryId"},{"type":"null"}]},"root_delivery_id":{"$ref":"#/components/schemas/WebhookDeliveryId"},"created_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"WebhookDeliveryListResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["items","pagination"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"pagination":{"$ref":"#/components/schemas/PaginationMeta"}},"additionalProperties":false}}},"WebhookRedeliveryRequest":{"type":"object","properties":{"reason":{"type":["string","null"],"maxLength":200}},"additionalProperties":false},"WebhookDeliveryResponse":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","const":true},"data":{"type":"object","required":["delivery"],"properties":{"delivery":{"$ref":"#/components/schemas/WebhookDelivery"}},"additionalProperties":false}}}}},"x-moddingflow-operation-contract":{"schemaVersion":1,"coverageMode":"all_public_operations_fail_closed","operationCount":47,"operations":["abortUploadSession","authorizeOAuthClient","completeUploadSession","createApiKey","createDeviceAuthorization","createModDraft","createModVersion","createUploadSession","createWebhookSubscription","exchangeOAuthToken","getApiKeyCapabilities","getGame","getJsonWebKeySet","getMod","getModVersion","getOAuthAuthorizationServerMetadata","getOidcDiscovery","getUploadSession","introspectOAuthToken","listAgentGatewayTools","listApiKeys","listCatalogCategories","listGames","listModDependencies","listModTeamMembers","listModVersions","listMods","listWebhookDeliveries","listWebhookSubscriptions","publishModVersion","redeliverWebhookDelivery","removeModTeamMember","resolveDownload","resolveDownloadFallback","resolveInstallPlan","revokeApiKey","revokeOAuthToken","rollbackModVersion","rotateApiKey","rotateWebhookSubscriptionSecret","runAgentGatewayTool","searchCatalog","setModTeamMember","signUploadParts","updateModDraft","updateModVersion","validateUploadRequest"],"requestExamples":"Required for every declared request body and every path, query, or header parameter; bodyless operations do not invent a request body.","successExamples":"Required on the primary 2xx response body, or on the Location header for a bodyless redirect.","deprecationMetadata":"Every operation declares an explicit OpenAPI deprecated boolean; active operations use false."}}