> ## Documentation Index
> Fetch the complete documentation index at: https://docs.app.strix.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Uploads

> Upload local repositories and documentation for whitebox scans

export const ScarfPixel = () => <img referrerPolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=831669b6-b4bd-42cb-8832-2122c03e1d5f" alt="" width="1" height="1" style={{
  position: "absolute",
  width: 0,
  height: 0,
  opacity: 0,
  pointerEvents: "none"
}} />;

<ScarfPixel />

With uploads, you can run whitebox pentests on code or documentation that is not connected through an SCM integration. You can let the CLI select and package a local working tree, or use the three upload API calls directly.

## Start a scan from local source

Review exactly what the CLI would send before uploading anything:

```bash theme={null}
strix cloud scans start --source . --dry-run --show-files --json
```

The dry run builds the same bounded ZIP locally and prints its manifest, selected paths, exclusion counts, compressed size, and SHA-256 digest. It neither uploads the archive nor starts a scan. For an agent or CI workflow, capture `source.archive_sha256` after reviewing the manifest, then approve that exact source snapshot:

```bash theme={null}
SOURCE_SHA256="<reviewed source.archive_sha256>"
strix cloud scans start --source . --approve-sha256 "$SOURCE_SHA256" --wait
```

Repeat the same `--source`, every `--exclude`, and all `--include-*` selection flags. The CLI rebuilds the archive and rejects the upload if the source or selection flags produce a different digest. Non-interactive and JSON runs never prompt; they accept either a matching `--approve-sha256` or deliberate one-shot `--yes`. The latter approves only the archive built in that invocation and is best suited to a human or one-shot workflow, not a digest-bound agent handoff.

In an interactive terminal, omitting both approval flags shows the source summary and asks `Upload this source and start the scan? [y/N]` for the current snapshot.

### Default privacy filters

The CLI includes only regular files and respects Git ignores when the source is in a Git worktree. It excludes these by default:

* version-control metadata such as `.git`, `.hg`, and `.svn`, plus symlinks and non-files
* hidden files and directories
* dependency, cache, coverage, and build directories such as `node_modules`, `vendor`, `venv`, `.venv`, `env`, `__pycache__`, `.tox`, `.pytest_cache`, `.mypy_cache`, `.ruff_cache`, `dist`, `build`, `coverage`, `target`, `.next`, `.nuxt`, and `.gradle`
* secret-like filenames such as `.env`, private keys, `.git-credentials`, application-default credentials, and service-account files
* common cloud, container, and cluster credential paths such as `.aws/credentials`, `.docker/config.json`, `.config/gcloud/credentials.db`, `.azure/accesstokens.json`, and `.kube/config`
* nested archives
* patterns in `.strixignore` and every repeated `--exclude GLOB`

Use `--include-hidden`, `--include-sensitive`, or `--include-archives` only after reviewing a dry run. These approvals are independent: a hidden secret-like file such as `.env` needs both `--include-hidden` and `--include-sensitive`. Version-control metadata, symlinks, and dependency/build output remain excluded. `.strixignore` is exclusion-only and does not support negated `!` patterns.

The CLI refuses a selection above 20,000 files, 25 MiB for any file, 250 MiB expanded in total, or 50 MiB for the compressed archive. It opens selected files without following symlinks and rejects replacements, appends, truncation, or metadata changes between selection and the completed archive read.

The platform validates repository ZIPs again while streaming and inflating them. It rejects malformed or empty ZIPs, absolute or parent-traversal paths, more than 20,000 files, entries over 25 MiB, more than 250 MiB expanded, or more than 100 MiB compressed. A rejected upload cannot be attached to a scan and is removed immediately or by cleanup.

### Scan type and credits

When `--engagement-type` is omitted, local source by itself infers `code_review`. Local source plus a domain infers `live_test`. An explicit engagement type takes precedence.

An upload is scan input, not a free scan. A non-Enterprise scan consumes its normal scope estimate; at the current default `ultra` tier, a source-only code review starts at 60 credits. Enterprise scans are plan-included. A request rejected before launch does not consume credits, and the CLI makes a best-effort deletion of the staged upload when scan creation fails. See [Billing and credits](/guides/billing).

### Cleanup and ambiguous launches

The CLI always removes its private temporary local archive. After a remote upload is staged, a definitive scan rejection causes the CLI to delete it. A network failure, `5xx` response, malformed success response, or interruption after launch begins is ambiguous because the platform may already have accepted the scan. In that case the CLI retains the upload and returns its `upload_id` with `launch_outcome_unknown: true`. If cleanup itself cannot be confirmed, it returns the retained ID with `cleanup_unknown: true`.

Check `strix cloud scans list` before retrying an ambiguous launch. If no scan uses the upload, remove it with `strix cloud uploads delete <UPLOAD_ID>`.

## Upload through the API

The direct API procedure has three steps: request a signed URL, upload the file directly to storage, and finalize the upload. Finalizing stores an upload for later use; start a scan with its ID in `upload_ids` to analyze it.

## 1. Request a signed upload URL

```bash theme={null}
curl -X POST "https://app.strix.ai/api/v1/uploads/request" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "file_name": "service.zip",
    "file_size": 1048576,
    "category": "repository"
  }'
```

The response contains an `upload_id`, `signed_url`, and upload `token`. Send the file to `signed_url` with `PUT`. Treat the short-lived upload token as a secret and do not log it. The `category` field is optional (`repository` or `documentation`). If you set `repository`, the file must be a ZIP code archive. If you omit the category, Strix selects it from the filename.

**Required scope:** `uploads:write`

## 2. Upload the file

Send the raw file bytes to the signed URL with `PUT`:

```bash theme={null}
curl -X PUT "<SIGNED_UPLOAD_URL>" \
  -H "Authorization: Bearer <UPLOAD_TOKEN>" \
  -H "Content-Type: application/zip" \
  --upload-file service.zip
```

## 3. Finalize the upload

```bash theme={null}
curl -X POST "https://app.strix.ai/api/v1/uploads/complete" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"upload_id": "<UPLOAD_ID>"}'
```

**Required scope:** `uploads:write`

The completed upload is not scanned automatically. Pass its ID as `upload_ids` when you create a scan. Delete unused uploads rather than leaving source artifacts attached to the workspace.

## Delete an upload

With the CLI:

```bash theme={null}
strix cloud uploads delete <UPLOAD_ID>
```

Or call the API directly:

```bash theme={null}
curl -X DELETE "https://app.strix.ai/api/v1/uploads/<UPLOAD_ID>" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>"
```

**Required scope:** `uploads:write`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.