Authentication
Musha has two kinds of callers: people using the app, and CI jobs uploading code. They use different credentials, and CI credentials are deliberately narrow.
CI credentials
A CI job needs to do one thing: create a scan. Both CI credentials can call POST /v1/scans and nothing else. They can't read findings, manage keys, change settings or see billing.
| Credential | Used by | Lifetime | Where it comes from |
|---|---|---|---|
OIDC token exchange, for a cit_... token | GitHub Actions, GitLab CI | 15 minutes | The job proves its identity with the platform's OIDC token. Musha checks it against the repository you connected |
Project-scoped CI key msk_live_... | Azure DevOps, Bitbucket | Up to one year | Created by an admin in CI keys |
Prefer OIDC when your CI supports it: there is no secret to leak, and the token can't be reused outside the job.
OIDC
The job posts its platform token to POST /v1/ci/token and receives a short-lived Musha token to use as the bearer token on POST /v1/scans. The GitHub action does this for you. Musha only issues the token if the repository and, if you pinned one, the ref match what you connected in the project's Integration guide.
Project-scoped CI keys
- In the app, open CI keys (admin only), or click Create CI key from a project's Integration guide.
- Pick the project. Every key belongs to exactly one project.
- Set an expiry date. It is required and can't be more than one year away.
- Copy the key. It is shown only once; Musha stores only a hash.
Send it as a bearer token:
Authorization: Bearer msk_live_...
| Property | Value |
|---|---|
| Scope | One project. A scan for any other project returns 403 with code PROJECT_SCOPE_MISMATCH |
| Allowed calls | POST /v1/scans only |
| Expiry | Required, one year at most |
| Limit | Five active keys per project |
| Rotation | Create the new key, update your CI secret, then revoke the old key |
Store keys in your CI's secret manager, never in source code.
People
The app signs people in with their account and uses a session for every other endpoint, such as listing scans or findings. These endpoints serve the app and are not part of the CI integration. There are no personal access tokens today.
Idempotency
POST /v1/scans requires an Idempotency-Key header with a UUID. Sending the same key again returns the original response instead of creating a second scan, which makes retries safe.
-H "Idempotency-Key: $(uuidgen)"
A missing or malformed key returns 400.
Request correlation
Responses include an X-Request-ID header. Include it when you contact support: it lets us find the exact request in our logs.
Errors
| Status | Meaning |
|---|---|
400 | A field is missing or invalid, or the Idempotency-Key is missing |
401 | The credential is missing, invalid, expired or revoked |
402 | The subscription is canceled (ACCOUNT_CANCELED) or suspended (ACCOUNT_SUSPENDED) |
403 | The credential is scoped to a different project (PROJECT_SCOPE_MISMATCH) |
404 | The project doesn't exist in your organization |
429 | A scan limit was reached. See Limits |