Upload API нужен авторам, командам, CI pipelines и tools, которым нужен Publish to Moddingflow без прохода через browser form на каждом release. Соберите архив локально или через GitHub Actions, загрузите его direct-to-cloud, дождитесь scan/validation и публикуйте new version только после статуса ready.
Используйте individual API keys или OAuth/PAT tokens вместо шаринга main account password. Перед созданием session CI может вызвать POST /v1/uploads:validate: validate-only проверка не создает upload и не пишет bytes. Используйте один стабильный Idempotency-Key на логическую загрузку: retry с тем же body возвращает ту же durable reservation без повторного списания byte quota, а изменённый body конфликтует. Большие архивы используют resumable multipart upload в Cloudflare R2: опрашивайте status.progress и completedParts, обновляйте URL только для missing parts и продолжайте ту же session до 48 часов, пока каждая signed URL остается короткоживущей. Если файл не пройдет validation, упрется в quota, повторит existing version или уйдет в quarantine/review, client получит machine-readable status, error code вроде duplicate_version и request_id для support; отмененная session возвращает lifecycleStatus cancelled. В production отсутствие clean malware verdict блокирует публикацию: явно опасные или заражённые архивы отклоняются, а nested archives, executables и непроверенные RAR/7z остаются в private quarantine до MFA-проверки staff. Account inbox пытается создать дедуплицированное уведомление о failed scan, required review и успешной публикации, но приход уведомления не является lifecycle acknowledgement; client должен продолжать polling machine-readable status.
Когда complete возвращает AsyncJob, сервис сначала изолирует загруженные bytes в private server-only seal; checksum, validation, scan и final copy читают этот immutable seal, а не client-writable staging key. Seal key не выдаётся клиенту и не требует управления. Опрашивайте job.statusEndpoint: queued и processing являются non-terminal; succeeded, rejected, failed и cancelled являются terminal. job.progress честно показывает completion 0/1 вместо примерного scanner percentage. Для диагностики ожидания и retry используйте createdAt, updatedAt, startedAt, completedAt, retry.attempt, retry.maxAttempts, retry.nextAttemptAt, retry.retryable и Retry-After. Terminal failure возвращает стабильный reason code, но не raw provider prose.
Для automation важен и HTTP-статус complete: queued или processing возвращаются как 202 Accepted с Location, равным job.statusEndpoint. Уже terminal job, включая replay того же завершённого результата с исходным Idempotency-Key, возвращается как 200 OK без повторения операции.
Каждый team member создает собственный resource-scoped key через POST /v1/api-keys и не получает owner session cookie. Create и rotate требуют Idempotency-Key. Повтор с тем же header и тем же request не выполняет credential mutation второй раз; поскольку raw secret никогда не сохраняется, завершённый replay возвращает idempotency_replay_unavailable без operation-shaped committed metadata. Проверьте key через GET /v1/api-keys или revoke его до новой mutation. Raw secret показывается one-time, сервер хранит только token_hash и safe fingerprint; GET /v1/api-keys/capabilities показывает scopes и projects. Роли owner, maintainer, publisher и reviewer ограничивают mods:write, versions:write, files:upload, versions:publish, versions:archive, files:manage и teams:admin. Rotation выполняется через POST /v1/api-keys/{api_key_id}/rotate, revoke - через DELETE /v1/api-keys/{api_key_id}, а last_used_at и key id/fingerprint остаются в audit без secret.