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

# Skill Scanner

# Skill Scanner API Documentation

Welcome to the Skill Scanner API documentation. This guide describes how to scan an **agent skill** for security threats: you point the scanner at a git repository and the path of a skill directory inside it, and it analyzes what that skill would do when an agent loads it.

## Purpose

An agent skill is instructions and files an agent picks up at runtime, usually from a public repository. That makes it a supply-chain surface: a skill can carry prompt injection, tell the agent to exfiltrate data, or ship a malicious binary alongside its instructions. The Skill Scanner fetches the skill, runs it through Enkrypt's skill-sentinel analysis, and returns a verdict, a risk level, a findings count and the full report — plus the repository's live GitHub star count, so you can weigh how widely a risky skill is already in use.

Scanning is **asynchronous**. `POST /skill-hub/scan` returns a `scan_id`; a scan usually finishes in 30–90 seconds. Poll `GET /skill-hub/scans/{scan_id}` until `status` is `succeeded` or `failed`.

## Offered APIs

* **scan**: Submit one skill (a `git_url` plus a `skill_path`) for scanning. Returns a `scan_id`.

* **get-scan**: Fetch one scan — its status and inputs while it runs, and the verdict, risk level, findings count, token usage and full report once it succeeds.

* **get-report**: Fetch only the report document for a completed scan, without the surrounding record.

* **list-scans**: List your scans as summaries (no reports), filterable by status, repository and verdict, with `limit`/`offset` paging.

* **health**: Liveness and configuration summary for the service.

## You see your organization's scans

Every scan is filed against the account behind the API key that submitted it, and that identity comes from your key, never from your request:

* For an **organization or project API key**, scans are filed against the **organization**. Every member sees every scan the organization has run, whichever project the key belongs to and whichever member submitted it. A skill someone else already scanned is a result you get for free rather than a scan you pay to repeat.
* For an **individual account**, it is simply your own scans.

What that means per endpoint:

* On **scan**, the gateway attaches the owning identity to the record. Sending `user_email` yourself is a `400`, as are `user_id`, `org_id` and `project_name`.
* On **list-scans**, the filter is applied for you. There is no `user_email` parameter to pass.
* On **get-scan** and **get-report**, a scan belonging to a **different organization** returns `403` with `reason: "owner_mismatch"`. Inside your own organization, everything is readable.

## Pinning a commit, and the dedup cache

`commit` is optional but strongly recommended:

```http theme={"system"}
POST /skill-hub/scan
{
  "git_url": "https://github.com/affaan-m/ECC.git",
  "skill_path": ".agents/skills/api-design",
  "commit": "2bc924aa11bb22cc33dd44ee55ff6677889900aa"
}
```

It does two things. It fixes exactly which bytes were scanned, so the verdict stays meaningful after the branch moves on. And it makes the result cacheable: if an identical pinned input has already been scanned successfully, your request returns immediately with HTTP `200` and `cached: true` instead of re-running the scanner — recorded under your own account, so it still shows up in your listing.

Without `commit` (or with `ref` alone) there is nothing stable to key the cache on, and every request runs a fresh scan.

Pass `?force=true` to bypass the cache deliberately and re-scan a pinned input.

## Reading the outcome

Two different things can be called "failure", and they are reported differently:

| What happened                                                           | HTTP                          | How you see it                                                                           |
| ----------------------------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------- |
| The scan ran and the skill looks malicious                              | `200`                         | `status: "succeeded"`, and `verdict` / `risk_level` / `findings_count` describe the risk |
| The scan itself could not complete (clone timed out, repo too large, …) | `200`                         | `status: "failed"` and `error` explains why                                              |
| Your request was rejected, or the scan is not yours to read             | `400` / `401` / `403` / `404` | error envelope, no record                                                                |

So a `failed` scan is a normal `200` response carrying a `failed` record — not an HTTP error. Check `status` before reading `verdict`.

`status` is `queued` or `running` while the worker is busy; only `succeeded` and `failed` are terminal.

## Python SDK

The [Python SDK](/sdk-reference/python/introduction) wraps all of this, including the polling loop:

```python theme={"system"}
from enkryptai_sdk import SkillScannerClient

client = SkillScannerClient(api_key="YOUR_API_KEY")

queued = client.scan({
    "git_url": "https://github.com/affaan-m/ECC.git",
    "skill_path": ".agents/skills/api-design",
    "commit": "2bc924aa11bb22cc33dd44ee55ff6677889900aa",
})

record = client.wait_for_scan(queued.scan_id)

if record.succeeded:
    print(record.verdict, record.risk_level, record.findings_count, record.stars)
    report = client.get_report(record.scan_id)
else:
    print("scan failed:", record.error)
```
