Scans API
Create a scan
POST /v1/scans
Uploads code for analysis. This is the only endpoint CI credentials can call. The request is multipart/form-data.
The scan runs while the request is open, so the response already carries the result.
Request
curl --fail-with-body -X POST "https://api.mushasec.com/v1/scans" \
-H "Authorization: Bearer $MUSHA_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-F "project_id=<project-id>" \
-F "scan_type=full" \
-F "branch=feature/auth" \
-F "pr_id=42" \
-F "commit_hash=<full-sha>" \
-F "files=@scan.tar.gz"
In practice you rarely write this by hand: the GitHub action and the generated pipelines build it for you, including added_lines.
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer plus a CI credential |
Idempotency-Key | Yes | A UUID. Reusing it returns the original response |
Form fields
| Field | Required | Description |
|---|---|---|
project_id | Yes | UUID of the project. Must match the credential's project |
scan_type | Yes | full, or any comma-separated mix of sca, iac and secrets (for example sca,iac) |
branch | Yes | Branch name |
files | Yes | The code as a gzipped tar archive (.tar.gz) |
commit_hash | No | SHA of the commit being scanned |
pr_id | No | Pull or merge request number. Needed for Musha to post the comment |
added_lines | No | JSON object of the lines the pull request added. See below |
Limits
| Limit | Value |
|---|---|
| Request size | 50 MB |
| Files in one request | 50 |
| Scans per hour, per organization | Trial 30, Starter 100, Team 500, Business 2,000, Enterprise 5,000 |
| Scans per month | Trial 50, Starter 500, Team 5,000, Business and Enterprise unlimited |
Reaching a limit returns 429 with a code such as QUOTA_EXCEEDED. When you hit the monthly limit you get a short grace period before new scans pause, and Musha never charges for extra scans. See Pricing.
added_lines
Tells Musha which lines the pull request added, so it can tell new findings from technical debt. It maps each file path to a list of [start, end] line ranges:
{ "src/api/auth.go": [[10, 24], [40, 41]], "go.mod": [[7, 7]] }
Without it, or if the diff couldn't be computed, Musha falls back to a repository-wide view and does not conclude that the pull request touched nothing.
Response
201 Created when the scan completed, 500 when it failed.
{
"scan_id": "<scan-id>",
"status": "completed",
"findings_count": 12,
"severity_breakdown": { "critical": 1, "high": 3 },
"blocked": true,
"untracked_count": 2,
"branch": "feature/auth",
"pr_id": "42",
"commit_hash": "<full-sha>"
}
blocked is true when the scan has findings your policy blocks on. The HTTP status does not change with it: a blocked scan is still 201. The GitHub action fails the job on blocked; for other CI, see GitLab CI.
Reading scans
GET /v1/scans, GET /v1/scans/:id and the related endpoints list and show scans. They serve the app and need a signed-in user, so CI credentials can't call them. Use CI scans in the app.
Scan statuses
| Status | Meaning |
|---|---|
pending | Waiting to run |
completed | Finished; findings are available |
failed | The scan failed. See scan_warnings in the scan detail |