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

# New api

# Corgea API Reference

This document provides detailed information about the Corgea API endpoints. The API allows you to programmatically interact with Corgea's scanning and vulnerability management features.

## Authentication

Most endpoints require authentication using a Corgea token that should be included in the `CORGEA-TOKEN` header. You can obtain a token from your Corgea account settings.

## Endpoints

### Verify Token API

Description: Verifies the provided token and returns the status.
Endpoint: `/verify/<str:token>`
HTTP Method: GET
Request Headers: None
Request Parameters:
Path Parameters:

* `token` (string): The token to be verified.
  Query Parameters: None
  Request Body: None
  Response:
  Status Codes:
* 200: Token is valid.
* 400: Invalid token.
  Response Body:

```json theme={null}
{
  "status": "ok"
}
```

or

```json theme={null}
{
  "status": "error"
}
```

Example Usage:

```bash theme={null}
curl https://www.corgea.app/api/v1/verify/your_token_here
```

### Scan Upload API

Description: Uploads a scan report for a given run ID.
Endpoint: `/scan-upload`
HTTP Method: POST
Request Headers:

* `CORGEA-TOKEN`: API key for authentication.
  Request Parameters:
  Query Parameters:
* `run_id` (string): The ID of the run associated with the scan report.
* `engine` (string): The engine used for the scan.
* `project` (string): The name of the project.
  Request Body: The scan report file.
  Response:
  Status Codes:
* 200: Scan report uploaded successfully.
* 400: Invalid request or content.
  Response Body:

```json theme={null}
{
  "status": "ok"
}
```

or

```json theme={null}
{
  "status": "invalid content"
}
```

Example Usage:

```bash theme={null}
curl -X POST https://www.corgea.app/api/v1/scan-upload?run_id=123&engine=corgea&project=myproject \
  -H "CORGEA-TOKEN: Bearer your_api_key" \
  --data-binary "@scan_report.txt"
```

### Start Scan API

Description: Initiates a new scan or continues an existing scan with a transfer ID. The Start Scan API supports chunked file uploads for large codebases by allowing you to split the upload into multiple requests.

For new scans, you must first initiate the scan with a POST request. Currently, only "blast" scan type is supported, which requires uploading a single source code file. Your company account must have blast scanning enabled to use this feature.

To upload a file, it must be divided into chunks. You can continue the upload process using PATCH requests with a transfer ID. Each chunk should be sent as a 'chunk\_data' file parameter.

Endpoint:

* `/start-scan` (for new scans)
* `/start-scan/<str:transfer_id>/` (for continuing existing scans)

HTTP Method:

* POST (for new scans)
* PATCH (for continuing existing scans)
* HEAD (to check upload status)

Request Headers:

* `CORGEA-TOKEN`: API key for authentication.

Request Parameters:
Path Parameters:

* `transfer_id` (string, optional): The transfer ID for continuing an existing scan upload.

Query Parameters: None

Request Body:
For new scans (POST):

* `scan_type` (string): Currently only "blast" is supported.
* `repo_url` (string): The URL of the repository.
* `branch` (string): The branch to be scanned.
* `sha` (string): The commit SHA to be scanned.
* `files` (file): Single source code file to be scanned. Multiple files are not supported.

For continuing existing scans (PATCH):

* `chunk_data` (file): The next chunk of data for the scan.
* `metadata` (string, optional): User-supplied scan metadata as a JSON object string, for example `{"pipeline_url": "https://ci.example/run/123"}`. It is validated on every chunk request and attached to the scan once the upload completes, so send the same value with each chunk. The value must be a JSON object of at most 16,384 bytes.

Response:
Status Codes:

* 200: Scan initiated or continued successfully.
* 400: Invalid request or content. Common errors include:
  * Blast scanning not enabled for your account
  * No files uploaded
  * Multiple files uploaded (only single file supported)
  * Invalid scan type (only "blast" supported)
  * Missing chunk\_data file
  * Malformed `metadata` (not a JSON object) or `metadata` larger than 16,384 bytes
  * Other scan initialization failures

<ResponseField name="Code" type="Sample Python code for chunk upload">
  <Expandable title="code">
    ```import os theme={null}
    import requests
    import argparse

    # This utility file is used to test chunk upload functionality.

    # Usage: 
    # Full Scan
    # python api/tests/test_upload_in_chunks.py --branch branchX --repo_url https://github.com/repo123 --sha 21jio112j3 --project_name projectX /path/to/your/file.zip

    # Partial Scan:
    # python api/tests/test_upload_in_chunks.py --branch branchX --repo_url https://github.com/repo123 --sha 21jio112j3 --project_name projectX --files_to_scan "vuln.py, test.py" --partial_scan true /path/to/your/file.zip

    # Configuration
    API_BASE_URL = "https://www.corgea.app/api/v1/start-scan"

    url_base_params = {"scan_type": "blast"} 
    token_headers = {"CORGEA-TOKEN": "<YOUR_TOKEN>"} 

    LARGE_CHUNK_SIZE = 5 * 1024 * 1024  # 5 MB chunks
    SMALL_CHUNK_SIZE = int(0.01 * 1024 * 1024)  # 10 KB is approximately 0.01 MB
    CHUNK_SIZE = LARGE_CHUNK_SIZE

    def initiate_upload(file_path):
        """Initiate the chunked upload by sending file metadata and a blank chunk."""
        file_name = os.path.basename(file_path)
        file_size = os.path.getsize(file_path)

        metadata = {
            'file_name': file_name,
            'file_size': file_size
        }
        print(f"Sending metadata for {file_name}:")
        
        blank_chunk = b'' 
        files = {'files': (file_name, blank_chunk)}
        
        response = requests.post(API_BASE_URL, params=url_base_params, files=files, headers=token_headers, json=metadata)
        if response.status_code == 200:
            upload_response = response.json()
            transfer_id = upload_response.get("transfer_id")
            print(f"Upload initiated. Transfer ID: {transfer_id}. Message: {upload_response.get('message')}")
            return transfer_id
        else:
            print(f"Failed to initiate upload: {response.text}")
            return None

    def upload_chunk(file_path, transfer_id, chunk_offset, chunk_data, project_name, branch, repo_url, sha, partial_scan, files_to_scan):
        """Upload a single chunk."""
        headers = {
            'Upload-Offset': str(chunk_offset),
            'Upload-Length': str(os.path.getsize(file_path)),
            'Upload-Name': os.path.basename(file_path),
        }
        print(f"headers: {headers}")

        headers.update(token_headers)
        
        form_data = {
            'project_name': project_name,
            'branch': branch,
            'repo_url': repo_url,
            'sha': sha,
            'partial_scan': partial_scan, 
            'files_to_scan': files_to_scan
        }

        print(f"form_data: {form_data}")
        files = {
            'chunk_data': ('chunk', chunk_data, 'application/octet-stream'),
        }
        response = requests.patch(
            f"{API_BASE_URL}/{transfer_id}/",
            headers=headers,
            params=url_base_params,
            files=files,
            data=form_data,
        )
        if response.status_code == 200:
            print(f"Upload progress: {chunk_offset / os.path.getsize(file_path) * 100:.2f}% ({chunk_offset})")
            return True, response.json(), response.headers
        else:
            print(f"Failed to upload chunk at offset {chunk_offset}: {response.text}")
            return False, response.json(), response.headers

    def check_upload_status(transfer_id):
        """Check the status of the current upload."""
        response = requests.head(f"{API_BASE_URL}/{transfer_id}/", params=url_base_params, headers=token_headers)
        if response.status_code == 200:
            offset = response.headers.get('Upload-Offset')
            print(f"Current upload offset from response header 'Upload-Offset': {offset}")
            return int(offset)
        else:
            print(f"Failed to check upload status: {response.text}")
            return None

    def upload_file_in_chunks(file_path, project_name, branch, repo_url, sha, partial_scan=False, files_to_scan=[]):
        """Upload a file in chunks."""
        file_size = os.path.getsize(file_path)
        transfer_id = initiate_upload(file_path)
        if not transfer_id:
            return
        
        offset = check_upload_status(transfer_id) or 0
        remote_offset = 0
        print(f"Uploading file in chunks. Total size: {file_size}")
        
        with open(file_path, 'rb') as f:
            f.seek(offset)
            while offset < file_size and remote_offset < file_size:
                chunk_data = f.read(CHUNK_SIZE)
                success, response, response_headers = upload_chunk(file_path, transfer_id, offset, chunk_data, project_name, branch, repo_url, sha, partial_scan, files_to_scan)
                if not success:
                    print("Aborting upload due to an error.")
                    return
                offset += len(chunk_data)
                print(f"Expected to have written {offset}")

                if response_headers.get('Upload-Offset'):
                    remote_offset = int(response_headers.get('Upload-Offset', offset))

        print(f"Upload progress: 100% ({file_size}) {remote_offset}")
        print(f"File {file_path} uploaded successfully!")
        print(f"Scan ID: {response.get('scan_id')}")

    if __name__ == "__main__":
        parser = argparse.ArgumentParser(description="Upload a large file in chunks.")
        parser.add_argument(
            "file",
            type=str,
            help="Path to the file to be uploaded."
        )
        parser.add_argument(
            "--branch",
            type=str,
            required=False,
            help="Branch of the repository."
        )
        parser.add_argument(
            "--repo_url",
            type=str,
            required=False,
            help="URL of the repository."
        )
        parser.add_argument(
            "--sha",
            type=str,
            required=False,
            help="SHA of the project."
        )
        parser.add_argument(
            "--project_name",
            type=str,
            required=False,
            help="Name of the project."
        )
        parser.add_argument(
            "--partial_scan",
            type=bool,
            required=False,
            help="True if this is a partial scan."
        )
        parser.add_argument(
            "--files_to_scan",
            type=str,
            required=False,
            help="Comma-separated files to scan."
        )
        args = parser.parse_args()

        file_to_upload = args.file
        if not os.path.isfile(file_to_upload):
            print(f"Error: File '{file_to_upload}' does not exist.")
        else:
            print("Parameters received:")
            print(f"File to upload: {file_to_upload}")
            print(f"Project name: {args.project_name}")
            print(f"Branch: {args.branch}")
            print(f"Repo URL: {args.repo_url}")
            print(f"Files to scan: {args.files_to_scan}")
            print(f"Partial scan: {args.partial_scan}")
            upload_file_in_chunks(file_to_upload, args.project_name, args.branch, args.repo_url, args.sha, args.partial_scan, args.files_to_scan)
    ```
  </Expandable>
</ResponseField>

Here are some example responses for the `/start-scan` and `/start-scan/<str:transfer_id>/` endpoints. These responses cover the different scenarios handled by the endpoint, including successful scan initiation, file upload errors, invalid request methods, and upload status checks. The responses are formatted as JSON objects with appropriate status codes and error messages.

#### POST /start-scan (New Scan)

**Success Response (200 OK)**:

```json theme={null}
{
  "transfer_id": "c9b0a8c7-f9b4-4c10-9d58-cd4c7e1c9c52",
  "status": "success",
  "message": "Scan initiated successfully."
}
```

**Error Responses (400 Bad Request)**:

```json theme={null}
{
  "status": "error",
  "message": "Blast Scan is not enabled for your account."
}
```

```json theme={null}
{
  "message": "No files uploaded for blast. Please upload the required files.",
  "status": "error"
}
```

```json theme={null}
{
  "message": "Multiple files uploaded for blast. Please upload only one file.",
  "status": "error"
}
```

```json theme={null}
{
  "message": "Only Blast scan is currently enabled by the API.",
  "status": "error"
}
```

#### PATCH /start-scan/transfer\_id/ (Continue Existing Scan)

**Success Response (200 OK)**:

```json theme={null}
{
  "transfer_id": "c9b0a8c7-f9b4-4c10-9d58-cd4c7e1c9c52",
  "status": "success",
  "message": "Chunk uploaded successfully."
}
```

Response headers should include `Upload-Offset` looks like this

```
'Upload-Offset': '545220'
```

**Success Response (200 OK)**: Once the full chunk is uploaded, the response will include a `scan_id` instead of a `transfer_id`.
This occurs when the `Upload-Offset` in the response headers matches the `Upload-Length` in the request headers.
You can use the `scan_id` to retrieve more scan-specific details using other APIs.

```json theme={null}
{
  "scan_id": "1a5afaa3-72ac-458f-a492-ac40ffc88e76",
  "status": "success",
  "message": "Chunk uploaded successfully."
}
```

Request headers should look like

```
Upload-Offset: '545220',
Upload-Length: '578220',
Upload-Name: 'dvpwa.zip'
```

Response header should have

```
'Upload-Offset': '578220'
```

**Error Response (400 Bad Request)**:

```json theme={null}
{
  "message": "Invalid request: 'chunk_data' file not found.",
  "status": "error"
}
```

```json theme={null}
{
  "message": "Failed to start Corgea",
  "status": "error",
  "internal_detail": "Error message from ScanException"
}
```

#### HEAD /start-scan/transfer\_id/ (Check Upload Status)

**Success Response (200 OK)**:

```json theme={null}
{
  "transfer_id": "c9b0a8c7-f9b4-4c10-9d58-cd4c7e1c9c52",
  "status": "success",
  "message": "Chunk received."
}
```

**Error Response (400 Bad Request)**:

```json theme={null}
{
  "status": "error",
  "message": "Invalid transfer ID."
}
```

### Get Scans API

Description: Retrieves a list of scans for the authenticated user's company.
Endpoint: `/scans`
HTTP Method: GET
Request Headers:

* `CORGEA-TOKEN`: API key for authentication.
  Request Parameters:
  Query Parameters:
* `project` (string, optional): Filter scans by project name.
* `repo` (string, optional): Filter scans by repository URL substring.
* `branch` (string, optional): Filter scans by exact branch name.
* `pull_request_id` (string, optional): Filter scans by exact pull request or merge request identifier.
* `sha` (string, optional): Filter scans by exact commit SHA.
* `page` (integer, optional): The page number for pagination (default: 1).
* `page_size` (integer, optional): The number of results per page (default: 20, max: 50).
  Request Body: None
  Response:
  Status Codes:
* 200: Scans retrieved successfully.
  Response Body:

```json theme={null}
{
  "status": "ok",
  "page": 1,
  "total_pages": 2,
  "total_scans": 24,
  "scans": [
    {
      "id": "uuid",
      "engine": "corgea",
      "project": "myproject",
      "created_at": "2023-05-24T12:34:56.789Z",
      "repo": "https://github.com/example/repo",
      "branch": "main",
      "status": "complete",
      "pull_request_id": null,
      "git_sha": "abcdef0123456789",
      "metadata": {
        "pipeline_url": "https://ci.example/run/123",
        "artifact_version": "1.2.3"
      }
    },
    {
      "id": "uuid",
      "engine": "corgea",
      "project": "anotherproject",
      "created_at": "2023-05-23T09:08:07.654Z",
      "repo": "https://github.com/example/another-repo",
      "branch": "develop",
      "status": "scanning",
      "pull_request_id": "123",
      "git_sha": null,
      "metadata": null
    }
  ]
}
```

`git_sha` is the commit SHA recorded for the scan, and `metadata` holds the user-supplied metadata attached when the scan was started (for example via `corgea scan --metadata`). Both are `null` when no value was recorded.

`status` is one of `scanning`, `processing`, `complete`, or `incomplete`. The first two are in-flight states, `complete` means the scan finished, and `incomplete` means it failed.

Example Usage:

```bash theme={null}
curl https://www.corgea.app/api/v1/scans?project=myproject&page=1&page_size=10 \
  -H "CORGEA-TOKEN: Bearer your_api_key"
```

### Get Scan API

Description: Retrieves details of a specific scan.
Endpoint: `/scan/<str:scan_id>`
HTTP Method: GET
Request Headers:

* `CORGEA-TOKEN`: API key for authentication.
  Request Parameters:
  Path Parameters:
* `scan_id` (string): The ID of the scan.
  Query Parameters: None
  Request Body: None
  Response:
  Status Codes:
* 200: Scan details retrieved successfully.
* 404: Scan not found.
  Response Body:

```json theme={null}
{
  "id": "uuid",
  "project": "myproject",
  "repo": "https://github.com/example/repo",
  "branch": "main",
  "status": "complete",
  "engine": "corgea",
  "created_at": "2023-05-24T12:34:56.789Z",
  "time_taken": 132.4,
  "git_sha": "abcdef0123456789",
  "metadata": {
    "pipeline_url": "https://ci.example/run/123"
  }
}
```

or

```json theme={null}
{
  "status": "error",
  "message": "Scan doesn't exist"
}
```

Example Usage:

```bash theme={null}
curl https://www.corgea.app/api/v1/scan/scan_id_here \
  -H "CORGEA-TOKEN: Bearer your_api_key"
```

### Get Issues for Scan API

Description: Retrieves a list of issues for a specific scan.
Endpoint: `/scan/<str:scan_id>/issues`
HTTP Method: GET
Request Headers:

* `CORGEA-TOKEN`: API key for authentication.
  Request Parameters:
  Path Parameters:
* `scan_id` (string): The ID of the scan.
  Query Parameters:
* `page` (integer, optional): The page number for pagination (default: 1).
* `page_size` (integer, optional): The number of results per page (default: 20, max: 50).
  Request Body: None
  Response:
  Status Codes:
* 200: Issues retrieved successfully.
* 404: Scan not found.
  Response Body:

```json theme={null}
{
  "status": "ok",
  "issues": [
    {
      "id": "uuid",
      "classification": {
        "id": "CWE-123",
        "name": "Vulnerability Name",
        "description": "Vulnerability description"
      },
      "urgency": "high",
      "created_at": "2023-05-24T12:34:56.789Z",
      "status": "accepted_risk",
      "sla_status": "due",
      "location": {
        "file": {
          "name": "file.py",
          "language": "python",
          "path": "path/to/file.py"
        },
        "project": {
          "name": "myproject",
          "branch": "main",
          "git_sha": "abcd1234"
        },
        "line_number": 42
      },
      "auto_triage": {
        "false_positive_detection": {
          "status": "valid"
        }
      },
      "auto_fix_suggestion": {
        "status": "fix_available",
        "patch": {
          "diff": "--- a/path/to/file.py\n+++ b/path/to/file.py\n@@ -1,3 +1,4 @@\n ...",
          "explanation": "This patch fixes the vulnerability by..."
        }
      },
      "assigned_to": {
        "id": "42",
        "email": "assignee@example.com",
        "name": "Sam Dev"
      }
    }
  ],
  "page": 1,
  "total_pages": 1,
  "total_issues": 1
}
```

or

```json theme={null}
{
  "status": "error",
  "message": "Scan not found"
}
```

Example Usage:

```bash theme={null}
curl https://www.corgea.app/api/v1/scan/scan_id_here/issues?page=1&page_size=10 \
  -H "CORGEA-TOKEN: Bearer your_api_key"
```

### Get Issue API

Description: Retrieves details of a specific issue.
Endpoint: `/issue/<str:issue_id>`
HTTP Method: GET
Request Headers:

* `CORGEA-TOKEN`: API key for authentication.
  Request Parameters:
  Path Parameters:
* `issue_id` (string): The ID of the issue.
  Query Parameters:
* `show_full_code` (boolean, optional): Whether to include the full code in the response (default: false).
  Request Body: None
  Response:
  Status Codes:
* 200: Issue details retrieved successfully.
* 404: Issue not found.
  Response Body:

```json theme={null}
{
  "status": "ok",
  "issue": {
    "id": "uuid",
    "scan_id": "uuid",
    "status": "open",
    "urgency": "high",
    "created_at": "2023-05-24T12:34:56.789Z",
    "classification": {
      "id": "CWE-123",
      "name": "Vulnerability Name",
      "description": "Vulnerability description"
    },
    "location": {
      "file": {
        "name": "file.py",
        "language": "python",
        "path": "path/to/file.py"
      },
      "project": {
        "name": "myproject",
        "branch": "main",
        "git_sha": "abcd1234"
      },
      "line_number": 42
    },
    "details": {
      "explanation": "This vulnerability occurs because...\n- Reason 1\n- Reason 2"
    },
    "auto_triage": {
      "false_positive_detection": {
        "status": "valid",
        "reasoning": "The issue is a valid vulnerability because..."
      }
    },
    "auto_fix_suggestion": {
      "id": "uuid",
      "status": "fix_available",
      "patch": {
        "diff": "--- a/path/to/file.py\n+++ b/path/to/file.py\n@@ -1,3 +1,4 @@\n ...",
        "explanation": "This patch fixes the vulnerability by..."
      }
    },
    "assigned_to": {
      "id": "42",
      "email": "assignee@example.com",
      "name": "Sam Dev"
    }
  }
}
```

or

```json theme={null}
{
  "status": "error",
  "message": "Issue not found"
}
```

Example Usage:

```bash theme={null}
curl https://www.corgea.app/api/v1/issue/issue_id_here?show_full_code=true \
  -H "CORGEA-TOKEN: Bearer your_api_key"
```
