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

# Update pentest draft

> Change a pentest that hasn't started.

Updates a draft pentest. Fields in the request replace their saved values. Omitted fields keep their current values. You can't change a pentest after it starts.

> [!NOTE]
>
> **Scope required**: `write`. **Role**: member or above.

* `repos` replaces the saved repository list and must contain at least one entry. Changing it clears the estimate.
* `login_targets` replaces the full list. Send `[]` to remove all login targets. Sending the same site and login instructions preserves a successful login check. Changing either resets `login_check` to `unverified` until you check again.
* To fix a login that failed its check, send the site again with new `login_instructions`, then run [Check pentest logins](https://docs.hacktron.ai/api-reference/pentests/check-pentest-logins.md).
* To switch a draft from signed-in testing to testing without a login (or back), clear the other list in the same request, for example `"login_targets": []` with your `target_urls`.

Run the estimate again after changing the draft. [Start pentest](https://docs.hacktron.ai/api-reference/pentests/start-pentest.md) rejects a credit total that doesn't match the current estimate.


## OpenAPI

````yaml PATCH /pentests/{id}
openapi: 3.0.0
info:
  title: Hacktron REST API
  description: Public REST API for programmatic access to Hacktron
  version: '1.0'
  contact: {}
servers:
  - url: https://api.hacktron.ai/v1
security:
  - api-key: []
tags: []
paths:
  /pentests/{id}:
    patch:
      tags:
        - REST API - Pentests
      summary: >-
        Change a pentest that has not started. Changing its repositories clears
        the estimate.
      operationId: RestPentestController_update
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RestUpdatePentestDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestPentestResponseDto'
components:
  schemas:
    RestUpdatePentestDto:
      type: object
      properties:
        target_urls:
          description: Target URLs (pentest only)
          type: array
          items:
            type: string
        custom_context:
          type: string
          description: Custom context (pentest only)
        application_id:
          type: string
          description: iva application this scan targets (group of repos)
        login_targets:
          description: >-
            Deployed sites to test signed in, each with how to sign in. On
            update the list replaces the saved one, and [] removes them. A
            pentest tests live sites either all signed in or all without a login
            (target_urls). Check each login with POST
            /pentests/{id}/login-checks before starting.
          maxItems: 10
          type: array
          items:
            $ref: '#/components/schemas/RestLoginTargetDto'
        repos:
          type: array
          description: >-
            Repositories or uploaded archives to test, replacing the saved ones.
            Changing them clears the estimate, so estimate again before
            starting.
          items:
            oneOf:
              - $ref: '#/components/schemas/ConnectedRepoInputDto'
              - $ref: '#/components/schemas/PublicRepoInputDto'
              - $ref: '#/components/schemas/UploadRepoInputDto'
            discriminator:
              propertyName: source
              mapping:
                connected: '#/components/schemas/ConnectedRepoInputDto'
                public: '#/components/schemas/PublicRepoInputDto'
                upload: '#/components/schemas/UploadRepoInputDto'
          minItems: 1
    RestPentestResponseDto:
      type: object
      properties:
        scan_id:
          type: string
          format: uuid
          description: Stable pentest id; task_id changes when it starts
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
            - stopped
            - cancelled
            - skipped
            - pending_verification
            - pending_triage
            - draft
            - estimating
            - estimated
        repos:
          type: array
          items:
            $ref: '#/components/schemas/RepoDataDto'
        target_urls:
          type: array
          items:
            type: string
        login_targets:
          type: array
          items:
            $ref: '#/components/schemas/RestPentestLoginTargetDto'
        custom_context:
          type: string
          nullable: true
        application_id:
          type: string
          nullable: true
          format: uuid
        cost_estimation_id:
          type: string
          nullable: true
          format: uuid
        triage_enabled:
          type: boolean
          nullable: true
        credits_used:
          type: number
          nullable: true
      required:
        - scan_id
        - status
        - repos
        - target_urls
        - login_targets
        - custom_context
        - application_id
        - cost_estimation_id
        - triage_enabled
        - credits_used
    RestLoginTargetDto:
      type: object
      properties:
        site:
          type: string
          description: >-
            A deployed copy of the app to test signed in, e.g.
            https://staging.acme.dev
          maxLength: 2048
        login_instructions:
          type: string
          description: >-
            How to sign in: the login page, username, password and any steps.
            Stored encrypted and never returned.
          maxLength: 2000
      required:
        - site
        - login_instructions
    ConnectedRepoInputDto:
      type: object
      properties:
        include_paths:
          description: >-
            Whitebox scope: gitignore-style globs (relative to the repo root)
            that narrow the scan to matching paths. Empty means the whole
            repository.
          example:
            - services/gateway/**
          type: array
          items:
            type: string
        exclude_paths:
          description: >-
            Whitebox scope: gitignore-style globs removed from the scan after
            include_paths is applied; use `*.md` to drop a file type.
          example:
            - '**/tests/**'
            - vendor/
            - '*.md'
          type: array
          items:
            type: string
        source:
          type: string
          enum:
            - connected
        url:
          type: string
          description: Repository URL
          example: https://github.com/owner/repo
        branch:
          type: string
          description: Branch name, tag name, or commit SHA
        ref_type:
          type: string
          enum:
            - branch
            - tag
            - sha
          description: How `branch` was selected (UI hint for draft re-use)
        github_installation_id:
          type: number
          description: >-
            GitHub App installation id. Omitted for PAT-based connections (e.g.
            GitHub Enterprise), where the installation is resolved from the
            repository URL and the organization instead.
      required:
        - source
        - url
        - branch
    PublicRepoInputDto:
      type: object
      properties:
        include_paths:
          description: >-
            Whitebox scope: gitignore-style globs (relative to the repo root)
            that narrow the scan to matching paths. Empty means the whole
            repository.
          example:
            - services/gateway/**
          type: array
          items:
            type: string
        exclude_paths:
          description: >-
            Whitebox scope: gitignore-style globs removed from the scan after
            include_paths is applied; use `*.md` to drop a file type.
          example:
            - '**/tests/**'
            - vendor/
            - '*.md'
          type: array
          items:
            type: string
        source:
          type: string
          enum:
            - public
        url:
          type: string
          description: Repository URL
          example: https://github.com/owner/repo
        branch:
          type: string
          description: Branch name, tag name, or commit SHA
        ref_type:
          type: string
          enum:
            - branch
            - tag
            - sha
          description: How `branch` was selected (UI hint for draft re-use)
      required:
        - source
        - url
        - branch
    UploadRepoInputDto:
      type: object
      properties:
        include_paths:
          description: >-
            Whitebox scope: gitignore-style globs (relative to the repo root)
            that narrow the scan to matching paths. Empty means the whole
            repository.
          example:
            - services/gateway/**
          type: array
          items:
            type: string
        exclude_paths:
          description: >-
            Whitebox scope: gitignore-style globs removed from the scan after
            include_paths is applied; use `*.md` to drop a file type.
          example:
            - '**/tests/**'
            - vendor/
            - '*.md'
          type: array
          items:
            type: string
        source:
          type: string
          enum:
            - upload
        archive_id:
          type: string
          description: Scan archive ID
      required:
        - source
        - archive_id
    RepoDataDto:
      type: object
      properties:
        include_paths:
          description: >-
            Whitebox scope: gitignore-style globs (relative to the repo root)
            that narrow the scan to matching paths. Empty means the whole
            repository.
          example:
            - services/gateway/**
          type: array
          items:
            type: string
        exclude_paths:
          description: >-
            Whitebox scope: gitignore-style globs removed from the scan after
            include_paths is applied; use `*.md` to drop a file type.
          example:
            - '**/tests/**'
            - vendor/
            - '*.md'
          type: array
          items:
            type: string
        source:
          type: string
          enum:
            - connected
            - public
            - upload
        provider:
          type: string
          enum:
            - github
            - gitlab
            - bitbucket
            - azure
          readOnly: true
          description: Resolved by the server at scan start; ignored on input
        url:
          type: string
          description: Repository URL
          example: https://github.com/owner/repo
        branch:
          type: string
          description: Branch name, tag name, or commit SHA
        ref_type:
          type: string
          enum:
            - branch
            - tag
            - sha
          description: How `branch` was selected (UI hint for draft re-use)
        archive_id:
          type: string
          description: Scan archive ID
        github_installation_id:
          type: number
          description: GitHub App installation ID (connected repos only)
    RestPentestLoginTargetDto:
      type: object
      properties:
        site:
          type: string
        login_check:
          type: string
          enum:
            - unverified
            - pending
            - running
            - succeeded
            - failed
            - cancelled
            - expired
          description: >-
            Whether Hacktron could sign in with the login the site has now. A
            pass lasts an hour, and changing the site or login makes it
            unverified.
        failure_reason:
          type: string
          nullable: true
          description: Why the check failed, without credentials
      required:
        - site
        - login_check
        - failure_reason
  securitySchemes:
    api-key:
      type: apiKey
      in: header
      name: X-Api-Key

````

## Related topics

- [Create pentest draft](https://docs.hacktron.ai/api-reference/pentests/create-pentest.md)
- [Check pentest logins](https://docs.hacktron.ai/api-reference/pentests/check-pentest-logins.md)
- [Get Started](https://docs.hacktron.ai/mcp/get-started.md)
- [Discard pentest draft](https://docs.hacktron.ai/api-reference/pentests/discard-pentest.md)
- [Hacktron in Slack](https://docs.hacktron.ai/platform/slack-assistant.md)


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