NAV

Introduction

The Cobalt API gives you REST access to Organizations, Assets, Pentests, Findings, and Events.

API Versions

Authentication

Cobalt uses API tokens to allow access to various endpoints. You can create a new Cobalt API token from within your Cobalt profile.

Cobalt expects the API token to be included in all API requests to the server in a header that looks like the following:

Authorization: Bearer YOUR-PERSONAL-API-TOKEN

Most API calls are scoped to a specific organization, with the X-Org-Token header. We include that header, as needed, in our API calls.

To see which organizations you belong to:

Organizations

Get All Organizations

curl -X GET "https://api.cobalt.io/orgs" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN"

The above command returns JSON structured like this:

{
  "data": [
    {
      "resource": {
        "id": "or_Uevoq7MyoYsPT9NPc3conL",
        "name": "Acme Corp.",
        "token": "e9d6da*****************************0e8ad"
      },
      "links": {
        "ui": {
          "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
        }
      }
    },
    {
      "resource": {
        "id": "or_SnUYXDBYd2qbaDSuNRNa5q",
        "name": "Lorem Corp.",
        "token": "f1d6da*****************************0e8ad"
      },
      "links": {
        "ui": {
          "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
        }
      }
    }
  ],
  "pagination": {
    "next_page": "/orgs?cursor=a1b2c3d4",
    "prev_page": "/orgs?cursor=4d3c2b1a"
  }
}

This endpoint retrieves a list of organizations, or orgs, that you belong to. Save the token field to be used in your X-Org-Token header in subsequent calls in querying for assets, findings, pentests and events that belong to that organization.

HTTP Request

GET https://api.cobalt.io/orgs

URL Parameters

ParameterDefaultDescription
cursorN/AUsed for pagination. Example: https://api.cobalt.io/orgs?cursor=a1b2c3d4
limit10If specified, returns only a specified amount of organizations. Example: https://api.cobalt.io/orgs?limit=5

Response Fields

FieldDescription
idA unique ID representing the organization. Starts with or_
nameThe name of the organization
tokenThe organization token you’ll need in subsequent calls
links.ui.urlA link to redirect an authorized user to this organization in the Cobalt web application

Assets

Get All Assets

curl -X GET "https://api.cobalt.io/assets" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "data": [
    {
      "resource": {
        "id": "as_GZgcehapJUNh6mjNuqsE4T",
        "title": "Acme Corp. HR System",
        "description": "HR system of the Acme Corp. holding sensitive employee data",
        "asset_type": "web",
        "logo": "https://s3.amazonaws.com/acmecorp/uploads/attachment/file/12345/cat.jpeg?something=1",
        "technology_stack": [
          {
            "title": "React 18.0.0"
          }
        ],
        "attachments": [
          {
            "id": "at_LA5GcEL4HRitFGCHREqmzL",
            "file_name": "rainbow.jpeg",
            "download_url": "https://s3.amazonaws.com/acmecorp/uploads/attachment/file/12345/rainbow.jpeg?something=1"
          }
        ],
        "tags": [
          {
            "name": "tag1"
          }
        ]
      },
      "links": {
        "ui": {
          "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
        }
      }
    }
  ],
  "pagination": {
    "next_page": "/assets?cursor=a1b2c3d4",
    "prev_page": "/assets?cursor=4d3c2b1a"
  }
}

This endpoint retrieves a list of assets that belong to the organization specified in the X-Org-Token header.

HTTP Request

GET https://api.cobalt.io/assets

URL Parameters

ParameterDefaultDescription
cursorN/AUsed for pagination. Example: https://api.cobalt.io/assets?cursor=a1b2c3d4
limit10If specified, returns only a specified amount of assets. Example: https://api.cobalt.io/assets?limit=5
asset_typeN/AIf specified, returns assets that match asset_type. See Response Fields below for example asset_type values. Example: https://api.cobalt.io/assets?asset_type=web. Returns an empty list if no assets match the asset_type filter.
tags_contains_all[]N/AIf specified, returns assets that contain all matching tags. This query parameter can be specified multiple times. Returns an empty list if no matches are found. Example: https://api.cobalt.io/assets?tags_contains_all[]=third party&tags_contains_all[]=some-tag-id
sortN/AIf specified, returns assets sorted by one of the chosen parameters: asset_type. When defined, records are returned in ascending order by the sort parameter. To return in descending order, use a - before the sort parameter. Example: https://api.cobalt.io/assets?sort=-asset_type.

Response Fields

FieldDescription
idA unique ID representing the asset. Starts with as_
titleThe title of the asset; set by user creating the asset
descriptionA description of the asset; set by user creating the asset
asset_typeAn asset type, such as; api, cloud_config, external_network, internal_network, mobile, web, web_plus_api, web_plus_mobile, wireless_network, iot, thick_client, physical, other
logoA link pointing the location of the uploaded asset logo
technology_stackA list of technology stacks. Each element contains the title of the technology. Example: React 18.0.0.
attachmentsA list of asset attachments. Attachment download URLs are pre-authorized and will expire after 10 minutes.
tagsA list of tags. A tag has a name attribute. Example: [{"name": "third-party system-id"}, {"name": "some-tag-id"}]
links.ui.urlA link to redirect an authorized user to this asset in the Cobalt web application

Get an Asset

curl -X GET "https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "data": {
    "resource": {
      "id": "as_GZgcehapJUNh6mjNuqsE4T",
      "title": "Acme Corp. HR System",
      "description": "HR system of the Acme Corp. holding sensitive employee data",
      "asset_type": "web",
      "logo": "https://s3.amazonaws.com/acmecorp/uploads/attachment/file/12345/cat.jpeg?something=1",
      "technology_stack": [
        {
          "title": "React 18.0.0"
        }
      ],
      "attachments": [
        {
          "id": "at_LA5GcEL4HRitFGCHREqmzL",
          "file_name": "rainbow.jpeg",
          "download_url": "https://s3.amazonaws.com/acmecorp/uploads/attachment/file/12345/rainbow.jpeg?something=1"
        }
      ],
      "tags": [
        {
          "name": "tag1"
        }
      ]
    },
    "links": {
      "ui": {
        "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
      }
    }
  }
}

This endpoint retrieves a specific asset belonging to the organization specified in the X-Org-Token header.

HTTP Request

GET https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER

Response Fields

FieldDescription
idA unique ID representing the asset. Starts with as_
titleThe title of the asset; set by user creating the asset
descriptionA description of the asset; set by user creating the asset
asset_typeAn asset type, such as; api, cloud_config, external_network, internal_network, mobile, web, web_plus_api, web_plus_mobile, wireless_network, iot, thick_client, physical, other
logoA link pointing the location of the uploaded asset logo
technology_stackA list of technology stacks. Each element contains the title of the technology. Example: React 18.0.0.
attachmentsA list of asset attachments (including the logo). Attachment download URLs are pre-authorized and will expire after 10 minutes.
tagsA list of tags. A tag has a name attribute. Example: [{"name": "third-party system-id"}, {"name": "some-tag-id"}]
links.ui.urlA link to redirect an authorized user to this asset in the Cobalt web application

Create an Asset

curl -X POST "https://api.cobalt.io/assets" \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: application/vnd.cobalt.v2+json' \
  -H 'Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-DUPLICATION' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN' \
  --data '{
            "title": "Test Asset",
            "description": "Lorem ipsum",
            "asset_type": "web",
            "tags": []
          }'

The above command returns no data and a 201 response code when successful. There will be a Location header pointing at the newly created asset.

This endpoint creates a new asset belonging to the organization specified in the X-Org-Token header.

HTTP Request

POST https://api.cobalt.io/assets

Body

FieldDescription
titleThe title of the asset; set by user creating the asset
descriptionOptional; A description of the asset; set by user creating the asset
asset_typeapi, cloud_config, external_network, internal_network, mobile, web, web_plus_api, web_plus_mobile, wireless_network, iot, thick_client, physical, or other
tagsOptional; A list of tags. A tag has a name attribute. Example: [{"name": "third-party system-id"}, {"name": "some-tag-id"}]. Defaults to empty list if not provided

Response

You get a 201 response code for a successful request. The Location response header contains the URL of the new asset within the Cobalt API.

Update an Asset

curl -X PUT 'https://api.cobalt.io/assets/AN-ASSET-IDENTIFIER' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: application/vnd.cobalt.v2+json' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN' \
  --data '{
            "title": "Updated title",
            "description": "Updated description",
            "asset_type": "web",
            "tags: []
          }'

The above command returns no data and a 204 response code when successful.

This endpoint updates an asset belonging to the organization specified in the X-Org-Token header.

HTTP Request

PUT https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER

Body

FieldDescription
titleThe title of the asset; set by user creating the asset
descriptionOptional; A description of the asset; set by user creating the asset
asset_typeOptions: api, cloud_config, external_network, internal_network, mobile, web, web_plus_api, web_plus_mobile, wireless_network, iot, thick_client, physical, other
tagsOptional; A list of tags. A tag has a name attribute. Example: [{"name": "third-party system-id"}, {"name": "some-tag-id"}]. If tags field is not provided in the PUT request, no changes will occur to the asset tags. Any tags that already exist for the asset and are not provided in the PUT request will be deleted. Any tags that already exist for the asset and are provided in the PUT request will remain. Any tags that do not exist for the asset and are provided in the PUT request will be created.

Response

On a successful update, a 204 response code will be returned.

Delete an Asset

curl -X DELETE 'https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: application/vnd.cobalt.v2+json' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN'

The above command returns no data and a 204 response code when successful.

This endpoint deletes an asset belonging to the organization specified in the header. Note that deleting an asset will also delete all associated tags for the asset.

HTTP Request

DELETE https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER

Response

On successful deletion, a 204 response code will be returned.

Upload an Attachment

curl -X POST 'https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER/attachments' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: multipart/form-data' \
  -H 'Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-DUPLICATION' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN'
  --form 'attachment=@"/path/to/image.jpg"'

The above command returns no data and a 201 response code when successful. There will be a Location header pointing at the newly created attachment.

This endpoint uploads a new attachment for an asset belonging to the organization specified in the X-Org-Token header.

HTTP Request

POST https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER/attachments

Body

Form fieldDescription
attachmentThe file to upload as an attachment.

File Requirements

Response

On successful upload, a 201 response code will be returned. A response header, Location, will contain the URL within Cobalt API of the new attachment which you can use only to DELETE the attachment.

Delete an Attachment

curl -X DELETE 'https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER/attachments/YOUR-ATTACHMENT-IDENTIFIER' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN'

The above command returns no data and a 204 response code when successful.

This endpoint deletes an attachment from an asset belonging to the organization specified in the header.

HTTP Request

DELETE https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER/attachments/YOUR-ATTACHMENT-IDENTIFIER

You can obtain this URL from the Location response header of the create attachment endpoint, or build it by getting the attachment identifier from the response data of the GET /assets endpoint.

Response

On successful deletion, a 204 response code will be returned.

curl -X POST 'https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER/logo' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: multipart/form-data' \
  -H 'Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-UPLOADS' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN'
  --form 'attachment=@"/path/to/image.jpg"'

The above command returns no data and a 201 response code when successful.

This endpoint updates the logo for an asset belonging to the organization specified in the X-Org-Token header. This means the old logo is removed and replaced by the new logo.

HTTP Request

POST https://api.cobalt.io/assets/YOUR-ASSET-IDENTIFIER/logo

Body

Form fieldDescription
attachmentThe file to upload as a logo.

File Requirements

Response

On successful upload, a 201 response code will be returned.

Pentests

Get All Pentests

curl -X GET "https://api.cobalt.io/pentests" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "data": [
    {
      "resource": {
        "id": "pt_JQJpAAMjyc8sVtXW2X2Aq5",
        "title": "HR System Security Test 2022-Q4",
        "objectives": "Coverage of OWASP top 10, ASVS and application logic.",
        "state": "new",
        "tag": "#PT5940",
        "asset_id": "as_4L4ZjKgfzP7VBwUmqCZmmL",
        "testing_type": "agile",
        "platform_tags": [
          "rails",
          "ruby",
          "aws"
        ],
        "methodology": "web",
        "targets": [
          "https://cobalt.io",
          "192.168.1.1"
        ],
        "start_date": "Dec 11 2019",
        "end_date": "Dec 25 2019"
      },
      "links": {
        "ui": {
          "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
        }
      }
    }
  ],
  "pagination": {
    "next_page": "/pentests?cursor=a1b2c3d4",
    "prev_page": "/pentests?cursor=4d3c2b1a"
  }
}

This endpoint retrieves a list of all pentests that belong to the organization specified in the X-Org-Token header.

HTTP Request

GET https://api.cobalt.io/pentests

URL Parameters

ParameterDefaultDescription
assetN/AIf specified, returns pentests scoped to this asset id. Example: https://api.cobalt.io/pentests?asset=as_GZgcehapJUNh6mjNuqsE4T or 404 if asset not found
cursorN/AUsed for pagination. Example: https://api.cobalt.io/pentests?cursor=a1b2c3d4
limit10If specified, returns only a specified amount of pentests. Example: https://api.cobalt.io/pentests?limit=5
sortN/AIf specified, returns pentests sorted by one of the chosen properties: start_date, or end_date. When defined, pentests are sorted in ascending order by the sort property. To sort in descending order, use a - before the sort property. Example: https://api.cobalt.io/pentests?sort=-start_date.
stateN/AIf specified, returns pentests that match state. See Response Fields below for example state values. Example: https://api.cobalt.io/pentests?state=new. Returns an empty list if no pentests match the state filter.
testing_typeN/AIf specified, returns pentests that match testing_type. See Response Fields below for example testing_type values. Example: https://api.cobalt.io/pentests?testing_type=agile. Returns an empty list if no pentests match the testing_type filter.
platform_tags_contains_all[]N/AIf specified, returns pentests that contain all specified platform tags. This query parameter can be specified multiple times. Returns an empty list if no matches are found. Example: https://api.cobalt.io/pentests?platform_tags_contains_all[]=Kotlin&platform_tags_contains_all[]=AWS
start_date_lteN/AIf specified, returns pentests where the start_date is less than or equal to the input date. Input format: YYYY-MM-DD. Returns an empty list if no pentests match the filter. Example: https://api.cobalt.io/pentests?start_date_lte=2021-04-16
start_date_gteN/AIf specified, returns pentests where the start_date is greater than or equal to the input date. Input format: YYYY-MM-DD. Returns an empty list if no pentests match the filter. Example: https://api.cobalt.io/pentests?start_date_gte=2021-04-16
end_date_lteN/AIf specified, returns pentests where the end_date is less than or equal to the input date. Input format: YYYY-MM-DD. Returns an empty list if no pentests match the filter. Example: https://api.cobalt.io/pentests?end_date_lte=2021-04-16
end_date_gteN/AIf specified, returns pentests where the end_date is greater than or equal to the input date. Input format: YYYY-MM-DD. Returns an empty list if no pentests match the filter. Example: https://api.cobalt.io/pentests?end_date_gte=2021-04-16

Response Fields

FieldDescription
idA unique ID representing the pentest. Starts with pt_
titleThe title of the returned pentest.
objectivesThe objectives of the pentest, for example, “Coverage of OWASP Top 10”
asset_idID of the asset that the returned pentest belongs to
testing_typePentest testing type, where can be: agile or comprehensive
platform_tagsTech stack of the target, for example, Java, Kotlin, Ruby, or AWS.
methodologyPentest methodology. Web, API, Web+API, Mobile, External Network and so on.
targetsTargetted IP addresses, domains, services, and so on.
start_dateThe starting date of the pentest. Format: Dec 11 2019
end_dateThe ending date of the pentest. Format: Dec 11 2019
statenew, in_review, planned, cancelled, live, remediation, or closed
links.ui.urlA link to redirect an authorized user to this pentest in the Cobalt web application

Get a Pentest

curl -X GET "https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "data": {
    "resource": {
      "id": "pt_JQJpAAMjyc8sVtXW2X2Aq5",
      "title": "HR System Security Test 2022-Q4",
      "objectives": "Coverage of OWASP top 10, ASVS and application logic.",
      "state": "new",
      "tag": "#PT5940",
      "asset_id": "as_4L4ZjKgfzP7VBwUmqCZmmL",
      "testing_type": "agile",
      "platform_tags": [
        "rails",
        "ruby",
        "aws"
      ],
      "methodology": "web",
      "targets": [
        "https://cobalt.io",
        "192.168.1.1"
      ],
      "start_date": "Dec 11 2019",
      "end_date": "Dec 25 2019"
    },
    "links": {
      "ui": {
        "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
      }
    }
  }
}

This endpoint retrieves a specific pentest that belongs to the organization specified in the X-Org-Token header.

HTTP Request

GET https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER-HERE

Response Fields

FieldDescription
idA unique ID representing the pentest. Starts with pt_
titleThe title of the returned pentest.
objectivesThe objectives of the pentest. for example “Coverage of OWASP Top 10”
asset_idID of the asset that the returned pentest belongs to
testing_typePentest testing type, where can be: agile or comprehensive
platform_tagsTech stack of the target. for example java, kotlin, ruby, aws, and so on.
methodologyPentest methodology. Web, API, Web+API, Mobile, External Network and so on.
targetsTargetted IP addresses, domains, services, and so on.
start_dateThe starting date of the pentest. Format: Dec 11 2019
end_dateThe ending date of the pentest. Format: Dec 11 2019
statenew, in_review, planned, cancelled, live, remediation, or closed
links.ui.urlA link to redirect an authorized user to this pentest in the Cobalt web application

Get a Pentest Report

curl -X GET "https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER/report" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "data": {
    "resource": {
      "id": "pt_JQJpAAMjyc8sVtXW2X2Aq5",
      "title": "Pentest Title",
      "state": "live",
      "asset": {
        "id": "as_4L4ZjKgfzP7VBwUmqCZmmL",
      },
      "pentesters": [
        {
          "username": "user1",
          "full_name": "User One"
        },
        {
          "username": "user2",
          "full_name": null
        }
      ],
      "report": {
        "report_state": "final",
        "title": "Pentest Title",
        "starts_at": "2022-06-09",
        "ends_at": "2022-06-13",
        "executive_summary": "A pentest.",
        "executive_analysis": "A pentest.",
        "scope_of_work": "Everything.",
        "summary_of_findings": "Some findings were found.",
        "summary_of_recommendations": "Fix some things."
      },
      "findings": {
        "severity": {
          "informational": [
            {
              "id": "vl_3sP2RCWWUajc3oRXmbQ4j2",
            },
            {
              "id": "vl_3sP2RCWWUajc3oRXmbQ4j3",
            }
          ],
          "low": [
            {
              "id": "vl_3sP2RCWWUajc3oRXmbQ4j4",
            }
          ],
          "medium": [],
          "high": [
            {
              "id": "vl_3sP2RCWWUajc3oRXmbQ4j5",
            }
          ],
          "critical": []
        },
        "state": {
          "new": [],
          "triaging": [
            {
              "id": "vl_3sP2RCWWUajc3oRXmbQ4j5",
            }
          ],
          "invalid": [
            {
              "id": "vl_3sP2RCWWUajc3oRXmbQ4j3",
            }
          ],
          "duplicate": [],
          "out_of_scope": [
            {
              "id": "vl_3sP2RCWWUajc3oRXmbQ4j2",
            }
          ],
          "need_fix": [],
          "wont_fix": [],
          "check_fix": [],
          "valid_fix": []
        }
      },
      "accepted_risks": [
        {
          "finding_id": "vl_VuLnerabiLityVuLnerab4",
          "accepted_risk_reason": "internal_dependencies",
          "state": "wont_fix"
        }
      ]
    },
    "links": {
      "ui": {
        "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
      }
    }
  }
}

This endpoint retrieves the report for a specific pentest that belongs to the organization specified in the X-Org-Token header. The pentest must be in one of the states live, remediation, or closed.

Note that being able to view a pentest does not equal the ability to view a pentest report. Pentest report visibility depends on a number of factors:

If this endpoint returns a 404 but the underlying pentest is viewable, it’s likely a permissions issue.

HTTP Request

GET https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER-HERE/report

Response Fields

FieldDescription
idA unique ID representing the pentest. Starts with pt_.
titleOptional; if present, the title of the returned pentest.
stateOne of live, remediation, or closed (reports can not be generated for pentests in other states).
assetOptional; if present, an object representing the asset associated with this pentest.
asset.idThe unique ID representing the asset associated with this pentest. Starts with as_.
pentestersOptional; if present, a list of the pentesters who performed this pentest.
reportOptional; if present, a summary of this pentest’s findings and recommendations.
findingsA list of the findings broken down by both severity and state. severity is not a required field so some findings may appear in the state section but not in severity. For a list of the valid severity keys, refer to the table in the Calculations section. For a list of valid state keys refer to the State section.
accepted_risksOptional; if present, a list of the accepted risk findings. For details, refer to the Accepted Risk Response Fields section below.
links.ui.urlA link to redirect an authorized user to this pentest in the Cobalt web application.

Pentester Response Fields

FieldDescription
usernameThe username of the pentester.
full_nameThe full name of the pentester; can be null.

Report Response Fields

FieldDescription
report_stateOptional; if present, one of new_state, draft, in_review, or final.
titleOptional; if present, the title of the pentest.
starts_atOptional; if present, the date the pentest starts.
ends_atOptional; if present, the date the pentest ends.
executive_summaryOptional; if present, a high-level overview of the pentest.
executive_analysisOptional; if present, a high-level analysis of the pentest.
scope_of_workOptional; if present, a description of the scope of work.
summary_of_findingsOptional; if present, a high-level summary of the findings.
summary_of_recommendationsOptional; if present, a high-level summary of the recommendations.

Accepted Risk Response Fields

FieldDescription
finding_idA unique ID representing the finding. Starts with vl_.
accepted_risk_reasonOne of low_severity, mitigated_by_waf, mitigated_by_other, no_longer_relevant, third_party_dependencies, internal_dependencies, intended_functionality, or other.
stateThe state of the finding.

Duplicate a Pentest

curl -X POST "https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER/duplicate" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Content-Type: application/vnd.cobalt.v2+json" \
  -H "Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-DUPLICATION" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

This endpoint creates a duplicate pentest based on the identifier provided. The pentest to be duplicated must exist and belong to the organization specified in the X-Org-Token header. Note there is no Request body required for this endpoint. Note that you cannot duplicate a pentest that is in Draft state. The new pentest will be in Draft state and will have the same brief as the pentest provided in the identifier.

HTTP Request

POST https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER-HERE/duplicate

Response

You get a 201 response code for a successful request. The Location response header contains the URL of the new pentest within the Cobalt API.

Create a Pentest

curl -X POST "https://api.cobalt.io/pentests" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "Content-Type: application/vnd.cobalt.v2+json" \
  -H "Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-DUPLICATION" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN" \
  --data '{
            "asset_id": "as_4L4ZjKgfzP7VBwUmqCZmmL",
            "title": "Pentest Title",
            "description": "Pentest description.",
            "technology_stack": "ruby, golang, rails, kotlin",
            "instructions": "The pentest target is the v1 endpoints",
            "additional_requests": "Do not test webhook endpoints.",
            "test_credentials": "Account: foobar, password: foobar",
            "test_credentials_option": "provided",
            "targets": "1.1.1.1, https://google.com",
            "testing_type": "comprehensive",
            "scoping": {
              "api": {
                "num_of_endpoints": 1,
                "num_of_roles": 1
              }
            }
          }'

The above command returns no data and a 201 response code when successful. There will be a Location header pointing at the newly created pentest.

This endpoint creates a new pentest from the information provided. The pentest will belong to the organization specified in the X-Org-Token header. The pentest will be in the Draft state.

HTTP Request

POST https://api.cobalt.io/pentests

Body

FieldDescription
asset_idThe ID of the asset being pentested; this asset must exist and belong to the organization specified in the X-Org-Token header.
titleThe title of the pentest.
descriptionA description of the pentest.
technology_stackA comma separated list of technologies in use; for example, “Ruby, Go, C#”
instructionsOptional; if present, instructions for the pentesters.
additional_requestsOptional; if present, special requests or instructions for the pentesters.
test_credentialsOptional; if present, credentials to be used by the pentesters.
test_credentials_optionOptional; if present, additional information about the credentials. Must be one of the following: provided, distributed, not_required
targetsOptional; if present, the URLs, endpoints, etc. being targeted by the pentest. A comma separated list; "1.1.1.1, https://cobalt.io"
testing_typeOptional; If present, this will be the type of the pentest. Must be one of the following: agile, comprehensive. Default: comprehensive
scopingOptional; if present, information about the size of the pentest. For details, refer to the Scoping Body Fields section below.

Scoping Body Fields

If scoping data is provided, it must match the type of the asset being pentested. For example, if the asset being pentested is of type API, the API-related scoping fields should be filled in.

FieldDescription
api.num_of_endpointsThe number of API endpoints to be pentested.
api.num_of_rolesThe number of roles to be pentested per API endpoint.
cloud_config.num_of_servicesThe number of cloud services to be pentested.
cloud_config.num_of_accountsThe number of roles to be pentested per cloud service.
mobile.num_of_viewsThe number of mobile app views to be pentested.
mobile.num_of_rolesThe number of roles to be pentested per mobile app view.
network.num_of_ipsThe number of network IP addresses to be pentested.
web.num_of_pagesThe number of web pages to be pentested.
web.num_of_rolesThe number of roles to be pentested per web page.

Response

You get a 201 response code for a successful request. The Location response header contains the URL of the new pentest within the Cobalt API.

Update a Pentest

curl -X PUT "https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "Content-Type: application/vnd.cobalt.v2+json" \
  -H "Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-DUPLICATION" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN" \
  --data '{
            "asset_id": "as_4L4ZjKgfzP7VBwUmqCZmmL",
            "title": "Pentest Title",
            "description": "Pentest description.",
            "technology_stack": "ruby, golang, rails, kotlin",
            "instructions": "The pentest target is the v1 endpoints",
            "additional_requests": "Do not test webhook endpoints.",
            "test_credentials": "Account: foobar, password: foobar",
            "test_credentials_option": "provided",
            "targets": "1.1.1.1, https://google.com",
            "testing_type": "comprehensive",
            "scoping": {
              "api": {
                "num_of_endpoints": 1,
                "num_of_roles": 1
              }
            }
          }'

The above command returns no data and a 204 response code when successful.

This endpoint updates a pentest from the information provided.

HTTP Request

PUT https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER

Body

FieldDescription
asset_idThe ID of the asset being pentested; this asset must exist and belong to the organization specified in the X-Org-Token header.
titleThe title of the pentest.
descriptionA description of the pentest.
technology_stackA description of the technology stacks in use; for example, C# or Ruby on Rails.
instructionsOptional; if present, instructions for the pentesters.
additional_requestsOptional; if present, special requests or instructions for the pentesters.
test_credentialsOptional; if present, credentials to be used by the pentesters.
test_credentials_optionOptional; if present, additional information about the credentials.
targetsOptional; if present, the URLs, endpoints, etc being targeted by the pentest.
testing_typeOptional; If present, this will be the new type of the pentest. Must be one of the following: agile, comprehensive.
scopingOptional; if present, information about the size of the pentest. For details, refer to the Scoping Body Fields section below.

Scoping Body Fields

If scoping data is provided, it must match the type of the asset being pentested. For example, if the asset being pentested is of type API, the API-related scoping fields should be filled in.

FieldDescription
api.num_of_endpointsThe number of API endpoints to be pentested.
api.num_of_rolesThe number of roles to be pentested per API endpoint.
cloud_config.num_of_servicesThe number of cloud services to be pentested.
cloud_config.num_of_accountsThe number of roles to be pentested per cloud service.
mobile.num_of_viewsThe number of mobile app views to be pentested.
mobile.num_of_rolesThe number of roles to be pentested per mobile app view.
network.num_of_ipsThe number of network IP addresses to be pentested.
web.num_of_pagesThe number of web pages to be pentested.
web.num_of_rolesThe number of roles to be pentested per web page.

Response

On success, a 204 response code will be returned.

Delete a Pentest

curl -X DELETE 'https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN'

The above command returns no data and a 204 response code when successful.

This endpoint deletes a pentest belonging to the organization specified in the header. Note that you can only delete a pentest when it is in draft or review state.

HTTP Request

DELETE https://api.cobalt.io/pentests/YOUR-PENTEST-IDENTIFIER

Response

On successful deletion, a 204 response code will be returned.

Findings

Get All Findings

curl -X GET "https://api.cobalt.io/findings" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "data": [
    {
      "resource": {
        "id": "vl_3sP2RCWWUajc3oRXmbQ4j9",
        "tag": "#PT3334_37",
        "title": "XSS vulnerability",
        "description": "Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts...",
        "type_category": "Cross-Site Scripting (XSS)",
        "labels": [
          {
            "name": "Your label"
          }
        ],
        "impact": 5,
        "likelihood": 4,
        "severity": "high",
        "affected_targets": [
          "https://example.com",
          "192.168.1.1"
        ],
        "proof_of_concept": "Here you can see...",
        "severity_justification": "The vulnerability can cause a lot of damage",
        "suggested_fix": "Ensure this...",
        "prerequisites": "Credentials are needed",
        "pentest_id": "pt_PEtv4dqnwGV2efZhLw3BM5",
        "http_request": "HTTP GET / ...",
        "asset_id": "as_HcChCMueiPQQgvckmZtRSd",
        "log": [
          {
            "action": "created",
            "timestamp": "2021-04-01T15:13:24.322Z"
          },
          {
            "action": "likelihood_changed",
            "value": 4,
            "timestamp": "2021-04-01T15:14:05.856Z"
          },
          {
            "action": "impact_changed",
            "value": 5,
            "timestamp": "2021-04-01T15:14:05.856Z"
          },
          {
            "action": "state_changed",
            "value": "need_fix",
            "timestamp": "2021-04-01T15:14:06.757Z"
          },
          {
            "action": "state_changed",
            "value": "check_fix",
            "timestamp": "2021-04-01T15:14:57.845Z"
          }
        ],
        "state": "check_fix",
        "created_at": "2022-09-26T18:35:18.759Z",
        "updated_at": "2022-09-26T18:36:57.462Z",
        "attachments": [
          {
            "id": "at_LA5GcEL4HRitFGCHREqmzL",
            "file_name": "rainbow.jpeg",
            "download_url": "https://s3.amazonaws.com/acmecorp/uploads/attachment/file/12345/rainbow.jpeg?something=1"
          }
        ]
      },
      "links": {
        "ui": {
          "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
        }
      }
    }
  ],
  "pagination": {
    "next_page": "/findings?cursor=a1b2c3d4",
    "prev_page": "/findings?cursor=4d3c2b1a"
  }
}

This endpoint retrieves a list of all pentest findings that belong to the organization specified in the X-Org-Token header, filterable by pentest_id or asset_id. The log array presents a history of each finding and corresponding timestamp.

Calculations

We follow the standard risk model described by OWASP, where:

Risk = Impact * Likelihood

Cobalt Risk Input Fields:

Cobalt Risk Classification (severity, a.k.a. criticality):

CategoryScoreDescription
critical25Includes vulnerabilities that require immediate attention.
high16-24Impacts the security of your application/platform/hardware, including supported systems. Includes high probability vulnerabilities with a high business impact.
medium5-15Includes vulnerabilities that are: medium risk, medium impact; low risk, high impact; high risk, low impact.
low2-4Specifies common vulnerabilities with minimal impact.
informational1Notes vulnerabilities of minimal risk to your business.

HTTP Request

GET https://api.cobalt.io/findings

URL Parameters

ParameterDefaultDescription
cursorN/AUsed for pagination. Example: https://api.cobalt.io/findings?cursor=a1b2c3d4
limit10If specified, returns only a specified amount of findings. Example: https://api.cobalt.io/findings?limit=5
pentestN/AIf specified, returns findings scoped to this pentest id. Example: https://api.cobalt.io/findings?pentest=pt_PEtv4dqnwGV2efZhLw3BM5
assetN/AIf specified, returns findings scoped to this asset id. Example: https://api.cobalt.io/findings?asset=as_HcChCMueiPQQgvckmZtRSd
stateN/AIf specified, returns findings that match state. See Response Fields below for example state values. Example: https://api.cobalt.io/findings?state=check_fix. Returns an empty list if no findings match the state filter.
severityN/AIf specified, returns findings that match severity. See Response Fields below for example severity values. Example: https://api.cobalt.io/findings?severity=medium. Returns an empty list if no findings match the severity filter.
labels_contains_all[]N/AIf specified, returns findings that contain all specified labels. This query parameter can be specified multiple times. Returns an empty list if no matches are found. Example: https://api.cobalt.io/findings?labels_contains_all[]=Awaiting Feedback&labels_contains_all[]=Retest Blocked
sortN/AIf specified, returns findings sorted by one of the chosen parameters: severity, impact, state, created_at and updated_at. When defined, findings are sorted in ascending order by the sort parameter. To sort in descending order, use a - before the sort parameter. Example: https://api.cobalt.io/findings?sort=-severity.
created_at_lteN/AIf specified, returns findings where the created_at timestamp is less than or equal to the input timestamp. ISO8601 is the supported input timestamp format. Returns an empty list if no findings match the filter. Example: https://api.cobalt.io/findings?created_at_lte=2020-02-20T15:28:10.335Z
created_at_gteN/AIf specified, returns findings where the created_at timestamp is greater than or equal to the input timestamp. ISO8601 is the supported input timestamp format. Returns an empty list if no findings match the filter. Example: https://api.cobalt.io/findings?created_at_gte=2020-02-20T15:28:10.335Z
updated_at_lteN/AIf specified, returns findings where the updated_at timestamp is less than or equal to the input timestamp. ISO8601 is the supported input timestamp format. Returns an empty list if no findings match the filter. Example: https://api.cobalt.io/findings?updated_at_lte=2020-02-20T15:28:10.335Z
updated_at_gteN/AIf specified, returns findings where the updated_at timestamp is greater than or equal to the input timestamp. ISO8601 is the supported input timestamp format. Returns an empty list if no findings match the filter. Example: https://api.cobalt.io/findings?updated_at_gte=2020-02-20T15:28:10.335Z

Response Fields

FieldEnum Types
logcreated, impact_changed, likelihood_changed, state_changed
severitynull, low, medium, high (aka criticality. will be null if likelihood/impact have not yet been set by the pentester)
severity_justificationOptional; The justification for the severity rating
prerequisitesOptional; The prerequisites required for reproducing the vulnerability
http_requestOptional; An example HTTP request for reproducing the vulnerability
statenew, triaging, need_fix, wont_fix, valid_fix, check_fix, invalid, carried_over
type_categoryXSS, SQLi, … (about 30 more via the Cobalt Taxonomy)
attachmentsA list of finding attachments. Attachment download URLs are pre-authorized and will expire after 10 minutes.
links.ui.urlA link to redirect an authorized user to this finding in the Cobalt web application

State

Get a Finding

curl -X GET "https://api.cobalt.io/findings/YOUR-FINDING-IDENTIFIER" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "resource": {
    "id": "vl_3sP2RCWWUajc3oRXmbQ4j9",
    "tag": "#PT5940",
    "title": "XSS vulnerability",
    "description": "Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts...",
    "type_category": "Cross-Site Scripting (XSS)",
    "labels": [
      {
        "name": "Your label"
      }
    ],
    "impact": 5,
    "likelihood": 4,
    "severity": "high",
    "affected_targets": [
      "https://example.com",
      "192.168.1.1"
    ],
    "proof_of_concept": "Here you can see...",
    "severity_justification": "The vulnerability can cause a lot of damage",
    "suggested_fix": "Ensure this...",
    "prerequisites": "Credentials are needed",
    "pentest_id": "pt_PEtv4dqnwGV2efZhLw3BM5",
    "http_request": "HTTP GET / ...",
    "asset_id": "as_HcChCMueiPQQgvckmZtRSd",
    "log": [
      {
        "action": "created",
        "timestamp": "2021-09-22T18:43:01.677Z"
      }
    ],
    "state": "new",
    "created_at": "2022-09-26T18:35:18.759Z",
    "updated_at": "2022-09-26T18:36:57.462Z",
    "attachments": [
      {
        "id": "at_LA5GcEL4HRitFGCHREqmzL",
        "file_name": "rainbow.jpeg",
        "download_url": "https://s3.amazonaws.com/acmecorp/uploads/attachment/file/12345/rainbow.jpeg?something=1"
      }
    ]
  },
  "links": {
    "ui": {
      "url": "https://api.cobalt.io/links/eyJ0eXBlIjoic29tZXRoaW5nIiwib3JnU2x1ZyI6ImNvYmFsdCIsInBlbnRlc3RUYWciOiJz="
    }
  }
}

This endpoint retrieves a specific finding that belong to the organization specified in the X-Org-Token header.

HTTP Request

GET https://api.cobalt.io/findings/YOUR-FINDING-IDENTIFIER

Response Fields

FieldEnum Types
logcreated, impact_changed, likelihood_changed, state_changed
severitynull, low, medium, high (aka criticality. will be null if likelihood/impact have not yet been set by the pentester)
severity_justificationOptional; The justification for the severity rating
prerequisitesOptional; The prerequisites required for reproducing the vulnerability
http_requestOptional; An example HTTP request for reproducing the vulnerability
statenew, triaging, need_fix, wont_fix, valid_fix, check_fix, invalid, carried_over
type_categoryXSS, SQLi, … (about 30 more via the Cobalt Taxonomy)
attachmentsA list of finding attachments. Attachment download URLs are pre-authorized and will expire after 10 minutes.
urlThe links.ui.url will redirect an authorized user to this finding in the Cobalt platform

State

View Available Finding States

curl -X GET "https://api.cobalt.io/findings/YOUR-FINDING-ID/possible_states" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "resource": {
    "current_state": "invalid",
    "possible_states": [
      "triaging",
      "need_fix",
      "duplicate",
      "out_of_scope"
    ]
  }
}

This endpoint retrieves the current state of a finding as well as possible next states.

HTTP Request

GET https://api.cobalt.io/findings/YOUR-FINDING-ID/possible_states

URL Parameters

ParameterDescription
YOUR-FINDING-IDA unique ID representing the organization. Starts with vl_

Response Fields

FieldDescription
current_stateThe current state of the finding.
possible_statesA list of states that the finding can be transitioned to.

States

Update Finding State

curl -X PATCH "https://api.cobalt.io/findings/YOUR-FINDING-ID" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H 'Content-Type: application/vnd.cobalt.v2+json' \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN" \
  -d '{"state":"triaging"}'

If successful, this command returns 204.

This endpoint updates the current state of a finding.

HTTP Request

PATCH https://api.cobalt.io/findings/YOUR-FINDING-ID

URL Parameters

ParameterDescription
YOUR-FINDING-IDA unique ID representing the organization. Starts with vl_

Body

FieldDescription
stateThe desired next state of the finding. Should be one of the possible states.

Events

Get All Events

curl -X GET "https://api.cobalt.io/events" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN"

The above command returns JSON structured like this:

{
  "data": [
    {
      "resource": {
        "id": "ac_Y35JcpGoakrjUSVjtVpXyH",
        "action": "comment_created",
        "subject": {
          "id": "ac_Y35JcpGoakrjUSVjtVpXyH",
          "type": "comment"
        },
        "timestamp": "2022-05-03T01:34:21.587Z"
      }
    },
    {
      "resource": {
        "id": "ac_Y35JcpGoakrjUSVjtVpXyP",
        "action": "pentest_deleted",
        "subject": {
          "id": "ac_Y35JcpGoakrjUSVjtVpXyP",
          "type": "program"
        },
        "timestamp": "2022-05-03T01:34:21.587Z"
      }
    },
    {
      "resource": {
        "id": "ac_Y35JcpGoakrjUSVjtVpXyX",
        "action": "finding_created",
        "subject": {
          "id": "ac_Y35JcpGoakrjUSVjtVpXyX",
          "type": "vulnerability"
        },
        "timestamp": "2022-05-03T01:34:21.587Z"
      }
    }
  ],
  "pagination": {
    "next_page": "/events?cursor=a1b2c3d4",
    "prev_page": "/events?cursor=4d3c2b1a"
  }
}

This endpoint retrieves a list of all events for your account.

HTTP Request

GET https://api.cobalt.io/events

URL Parameters

ParameterDefaultDescription
cursorN/AUsed for pagination. Example: https://api.cobalt.io/events?cursor=a1b2c3d4
limit10If specified, returns only a specified amount of events. Example: https://api.cobalt.io/events?limit=5

Tokens

Get All Tokens

curl -X GET "https://api.cobalt.io/tokens" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN"

The above command returns JSON structured like this:

{
  "data": [
    {
      "resource": {
        "id": "api_Dge3LsHMjtX8SGEk4a8nux",
        "last_characters": "9qy7",
        "name": "Lorem ipsum",
        "expire_at": null
      }
    }
  ],
  "pagination": {
    "next_page": null,
    "prev_page": null
  }
}

This endpoint retrieves a list of all tokens that belong to you.

HTTP Request

GET https://api.cobalt.io/tokens

URL Parameters

ParameterDefaultDescription
cursorN/AUsed for pagination. Example: https://api.cobalt.io/tokens?cursor=a1b2c3d4
limit10If specified, returns only a specified amount of tokens. Example: https://api.cobalt.io/tokens?limit=5

Response Fields

FieldDescription
idA unique ID representing the token. Starts with api_
nameName of the API token
last_charactersLast four characters of your token, so that you can recognize tokens even if they have the same name
expire_atnull (not currently implemented)

Refresh Token

curl -X POST "https://api.cobalt.io/tokens/YOUR-TOKEN-ID/refresh" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-DUPLICATION"

The above command returns JSON structured like this:

{
  "resource": {
    "id": "api_Dge3LsHMjtX8SGEk4a8nux",
    "secret": "YOUR-NEW-PERSONAL-API-TOKEN",
    "name": "Lorem ipsum",
    "expire_at": null
  }
}

You can revoke an existing token and issue a new one with the same name by making a POST request to the token refresh endpoint.

Process:

If you’ve forgotten your token, you can always re-authenticate in the Cobalt web app. Go to your profile, revoke the old token you’ve forgotten, and generate a new token.

HTTP Request

POST https://api.cobalt.io/tokens/YOUR-TOKEN-ID/refresh

Response Fields

FieldDescription
idA unique ID representing the new token. Starts with api_
secretYour new personal API token. Keep it safe and don’t share with anyone
nameName of your API token
expire_atnull (not currently implemented)

Webhooks

Webhooks allow your system to be notified when events occur on the Cobalt Platform via HTTP POST requests. This eliminates the need to poll the API for updates.

Get all webhooks

curl -X GET "https://api.cobalt.io/webhooks" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "pagination": {
    "next_page": null,
    "prev_page": null
  },
  "data": [
    {
      "resource": {
        "id": "wb_38URp3gxZqfkjEXmkybrrs",
        "name": "My Webhook",
        "url": "https://example.local",
        "active": true,
        "unhealthy_since": null,
        "user": "us_RxvqT5T3WCFrfTF74B6JLC",
        "subscribed_event_types": [
          "PENTEST_CREATED",
          "PENTEST_STATE_UPDATED",
          "FINDING_DELETED",
          "FINDING_PUBLISHED",
          "FINDING_STATE_UPDATED",
          "FINDING_UPDATED"
        ]
      }
    }
  ]
}

This endpoint retrieves a list of all webhooks that belong to your organization.

HTTP Request

GET https://api.cobalt.io/webhooks

URL Parameters

ParameterDefaultDescription
cursorN/AUsed for pagination. Example: https://api.cobalt.io/webhooks?cursor=a1b2c3d4
limit10If specified, returns only a specified amount of webhooks. Example: https://api.cobalt.io/webhooks?limit=5

Response Fields

FieldDescription
idThe ID of the webhook
nameThe name of the webhook
urlThe URL that webhook events are sent to
activeA boolean flag that indicates if the webhook is active
unhealthy_sinceThe time that we began failing to deliver events to this webhook. If the webhook is unhealthy, this field will contain an ISO8601 time stamp. Ex: 2022-08-30T14:14:14.000Z
userThe ID of the user that created the webhook
subscribed_event_typesThe event types that the webhook is subscribed to. See possible event types here. Webhook event filtering functionality is not yet enabled for customers.

Get a webhook

curl -X GET "https://api.cobalt.io/webhooks/YOUR-WEBHOOK-IDENTIFIER" \
  -H "Accept: application/vnd.cobalt.v2+json" \
  -H "Authorization: Bearer YOUR-PERSONAL-API-TOKEN" \
  -H "X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN"

The above command returns JSON structured like this:

{
  "resource": {
    "id": "wb_38URp3gxZqfkjEXmkybrrs",
    "name": "My Webhook",
    "url": "https://example.local",
    "active": true,
    "unhealthy_since": null,
    "user": "us_RxvqT5T3WCFrfTF74B6JLC",
    "subscribed_event_types": [
      "PENTEST_CREATED",
      "PENTEST_STATE_UPDATED",
      "FINDING_DELETED",
      "FINDING_PUBLISHED",
      "FINDING_STATE_UPDATED",
      "FINDING_UPDATED"
    ]
  }
}

This endpoint retrieves a specific webhook belonging to your organization.

HTTP Request

GET https://api.cobalt.io/webhooks/YOUR-WEBHOOK-IDENTIFIER

Response Fields

FieldDescription
idThe ID of the webhook
nameThe name of the webhook
urlThe URL that webhook events are sent to
activeA boolean flag that indicates if the webhook is active
unhealthy_sinceThe time that we began failing to deliver events to this webhook. If the webhook is unhealthy, this field will contain an ISO8601 time stamp. Example: 2022-08-30T14:14:14.000Z
userThe ID of the user that created the webhook
subscribed_event_typesThe event types that the webhook is subscribed to. See possible event types here. Webhook event filtering functionality is not yet enabled for customers.

Create a webhook

curl -X POST "https://api.cobalt.io/webhooks" \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: application/vnd.cobalt.v2+json' \
  -H 'Idempotency-Key: A-UNIQUE-IDENTIFIER-TO-PREVENT-UNINTENTIONAL-DUPLICATION' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN' \
  --data '{
            "name": "My Webhook",
            "active": true,
            "secret": "my_secret",
            "url": "https://example.local/webhook",
            "subscribed_event_types": [
              "FINDING_PUBLISHED"
            ]
          }'

The above command returns no data and a 201 response code when successful. There will be a Location header pointing at the newly created webhook.

This endpoint creates a new webhook belonging to your organization.

When you attempt to create a webhook, we will send a test event to your endpoint to validate that events can be delivered successfully. Your endpoint must respond with a successful HTTP response status code, for example, 200, 201, 204, etc. For details on test events, see the Webhook Events section below.

HTTP Request

POST https://api.cobalt.io/webhooks

Body

FieldDescription
nameThe name of the webhook
activeA boolean flag specifying if the webhook is active
secretAn arbitrary string value. We include this value in the X-Secret header when we send webhook events to you. You can use this to verify that the events you receive are from Cobalt. This field is optional.
urlThe URL to send events to
subscribed_event_typesOptional. The event types that the webhook should be subscribed to. Defaults to all event types when not specified. May not be an empty list. See possible event types here. This field may be used, however, webhook event filtering functionality is not yet enabled for customers.

Response

You get a 201 response code for a successful request. The Location response header contains the URL of the new webhook within the Cobalt API.

Update a webhook

curl -X PATCH 'https://api.cobalt.io/webhooks/YOUR-WEBHOOK-IDENTIFIER' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: application/vnd.cobalt.v2+json' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN' \
  --data '{
            "name": "FooBar",
            "secret": "super_secret",
            "active": false,
            "url": "https://example.local/webhook2",
            "subscribed_event_types": [
              "FINDING_PUBLISHED"
            ]
          }'

The above command returns no data and a 204 response code when successful.

This endpoint updates a webhook belonging to your organization.

HTTP Request

PATCH https://api.cobalt.io/webhooks/YOUR-WEBHOOK-IDENTIFIER

Body

All body fields are optional. You only need to include the fields that should be updated.

FieldDescription
nameThe name of the webhook
secretAn arbitrary string value. We include this value in the X-Secret header when we send webhook events to you. You can use this to verify that the events you receive are from Cobalt.
activeA boolean flag specifying if the webhook is active
urlThe URL to send events to
subscribed_event_typesThe event types that the webhook should be subscribed to. May not be an empty list. Non-specified event types that are currently subscribed to will be un-subscribed from. Specified event types that are not currently subscribed to will be subscribed to. See possible event types here. This field may be used, however, webhook event filtering functionality is not yet enabled for customers.

Response

On a successful update, a 204 response code will be returned.

Delete a webhook

curl -X DELETE 'https://api.cobalt.io/webhooks/YOUR-WEBHOOK-IDENTIFIER' \
  -H 'Accept: application/vnd.cobalt.v2+json' \
  -H 'Authorization: Bearer YOUR-PERSONAL-API-TOKEN' \
  -H 'Content-Type: application/vnd.cobalt.v2+json' \
  -H 'X-Org-Token: YOUR-V2-ORGANIZATION-TOKEN'

The above command returns no data and a 204 response code when successful.

This endpoint deletes a webhook belonging to your organization.

HTTP Request

DELETE https://api.cobalt.io/webhooks/YOUR-WEBHOOK-IDENTIFIER

Response

On successful deletion, a 204 response code will be returned.

Webhook Events

Webhook event properties:

FieldDescription
idThe ID of the webhook event
actionThe action that the event is related to
subjectThe subject that the event is related to
timestampThe time that the event ocurred

action types:

subject properties:

FieldDescription
idThe ID of the subject resource
typeThe type of the subject resource

subject types:

{
  "id": "eve_4tvptSU2SyqupRGQ4Jawdx",
  "action": "PENTEST_CREATED",
  "subject": {
    "id": "eve_4tvptSU2SyqupRGQ4Jawdx",
    "type": "PENTEST"
  },
  "timestamp": "2022-09-17T17:14:06.734Z"
}

Webhook Delivery and Health

Delivery process:

If your webhook becomes deactivated then you will need to activate it again via the API.

Best Practices

  1. Set your webhook secret to a high-entrophy value of sufficient length. When you receive an event, check that the value in the X-Secret header matches your secret. This ensures that you do not process fraudulent webhook events from a threat actor.
  2. Don’t add expensive operations to the endpoint that receives webhook events. A common pattern is to receive webhook events with a lightweight endpoint that publishes received events to a message queue that can be processed by your components containing business logic. This keeps the latency and failure rate of your webhook endpoint low.

Pagination

Pagination can be used if the number of resources for a request exceeds the value of the limit query parameter of the request. If the next_page and/or prev_page values in the response are non-null there are additional resources. To paginate append the next_page or prev_page value to the base API URL.

For example, if the next_page value in a response is /resource?cursor=a1b2c3d4 you can send the following request for the next page.

GET https://api.cobalt.io/resource?cursor=a1b2c3d4

Idempotency

The API supports idempotency for safely retrying requests without accidentally performing the same operation twice. This is useful when an API call is disrupted in transit and you do not receive a response. For example, if a request to create an asset does not respond due to a network connection error, you can retry the request with the same idempotency key to guarantee that no more than one asset is created.

To perform an idempotent request, provide an additional Idempotency-Key: <key> header to the request. The header name Mutation-Check is also supported for backwards compatibility with previous versions of the API.

An idempotency key is a unique value generated by the client which the server uses to recognize subsequent retries of the same request. How you create unique keys is up to you, but we suggest using V4 UUIDs, or another random string with enough entropy to avoid collisions.

All POST requests optionally accept idempotency keys. Idempotency keys expire after 5 minutes.

Rate Limits

The Cobalt API sets rate limits on API requests to manage request volume and maintain system availability.

Rate Limits

Request TypeHTTP VerbsLimitWindowHTTP Response Headers
Get requestsGET10060 secondsX-Rate-Limit-Get-Limit and X-Rate-Limit-Remaining
Mutate requestsPOST, PATCH, PUT, DELETE2060 secondsX-Rate-Limit-Mutate-Limit and X-Rate-Limit-Remaining

HTTP Response Headers

When the Rate Limit Is Exceeded

When a user exceeds the rate limit, the API returns a 509 BANDWIDTH_LIMIT_EXCEEDED HTTP response code along with the headers above. The rate limit resets after the specified time window.

Errors

The Cobalt API uses the following error codes:

Error CodeMeaning
400Bad Request – Your request is not good
401Unauthorized – Your API token is wrong
403Forbidden – You don’t have access to this resource
404Not Found – The specified request could not be found
405Method Not Allowed – You tried to access Cobalt data with an invalid method
406Not Acceptable – You requested a format that isn’t json
409Conflict – You attempted to create a resource with the same Idempotency-Key header as a recent request.
410Gone – The requested endpoint has been removed from Cobalt servers
418I’m a teapot
422Unprocessable Entity – The content and syntax are correctly formed, but something else is off.
429Too Many Requests – You’re making requests too often.
500Internal Server Error – We had a problem with our server. Try again later.
503Service Unavailable – We’re temporarily offline for maintenance. Please try again later.

Partner Integrations

We request that you please set a custom User-Agent header when developing a partner integration. The custom User-Agent header should identify your integration. This allows us to identify traffic originating from your integration for analytical and troubleshooting purposes.

Format: User-Agent: [IntegrationName]/[IntegrationVersion]

Example: User-Agent: PartnerCobaltIntegration/1.0.0

The User-Agent header is not required, but we strongly encourage partner integrations to send this header.

Changelog

New Endpoints

In the v2 release, we’ve published some additional endpoints to extend the capabilities of Cobalt API. Please refer to the following endpoints for more details:

Identifier Changes

In Cobalt API v2, we’ve focused on standardizing and extending identifiers for all endpoints. As a result, either the structure or the length of some identifiers has changed. You can see a side-by-side comparison of v1 and v2 with some sample identifiers below:

EndpointResponse fieldv1 (example)v2 (example)
organizationsidor_A2bb4FEor_Uevoq7MyoYsPT9NPc3conL
organizationstokenABCDEFGHJ12345678901ASDFGHJKLQWERTYUM1234567890ABCDEFGH1234567891234
assetsidas_rvZRC5Yas_GZgcehapJUNh6mjNuqsE4T
assetsattachments.tokenatt_yYXZodAat_LA5GcEL4HRitFGCHREqmzL
pentestsidpt_rVShby8pt_JQJpAAMjyc8xVtXW2X2Aq5
findingsidvu_ZzZuekbvl_3xP2RCWWUajc3oRXmbQ4j9
findingspentest_idpt_9Ig1234pt_PEtv4xqnwGV2efZhLw3XM5
findingsasset_idas_cwrsqsLas_HcChCMueiPQQgvckmZtRSd
eventsid277603ac_Y35JcpGoakrjUSVjtVpXyH
eventssubject.id277603ac_Y35JcpGoakrjUSVjtVpXyH

For the majority of endpoints, the only change is the length of the identifier. However, there are some prefix and type changes too. For example, the identifier prefix of the findings endpoint has changed from vu to vl. Similarly, the attachments.token prefix has changed from att to at.

We aren’t expecting any of these to be breaking changes for the majority of our customers, but, if you have any validations in place, concerning prefixes or the length of strings, please update them accordingly.

Please note - the token attribute of the organizations endpoint now returns a different string in v2. This value is also known as “organization token” all over the API docs, and is used as the value of X-Org-Token header when calling endpoints.

Renamed Response Attributes

The token attribute of the attachments object in v1 of assets endpoint was renamed to id in v2.

{
  // v1
  "attachments": [
    {
      "token": "att_yYXZodA"
    }
  ],

  // v2
  "attachments": [
    {
      "id": "at_LA5GcEL4HRitFGCHREqmzL"
    }
  ]
}

New Response Attributes

The response from listing findings and getting a single finding now includes an attachments attribute that shows files attached to a finding. Finding attachments can be programmatically downloaded using this information. Click here for more information.

Request Headers

Headerv1v2Description
Acceptapplication/vnd.cobalt.v1+jsonapplication/vnd.cobalt.v2+jsonMust be present in the request
Content-TypeN/Aapplication/vnd.cobalt.v2+jsonRequired for POST/PUT/DELETE HTTP methods
Idempotency-KeyN/ARefer to idempotencySuggested for POST requests

In v1, the Cobalt API was read-only, and in v2 we’ve added different endpoints where you can create, update or delete resources.

With these additions in place, two new headers came into place; Content-Type and Idempotency-Key. The Idempotency-Key is explained in the idempotency section.

Pagination Defaults

In v1, some endpoints had 10, and some others had 1000 as their pagination default value. In this release, we’ve updated the default pagination values of all endpoints to 10. Two endpoints were affected by this change:

Endpointv1 (default)v2 (default)
pentests100010
findings100010

Subscription

If you want to receive our API updates you can subscribe to our mailing list. We will be sending you a newsletter containing changes and improvements in our API once a month. You can also unsubscribe anytime you want. Your email address won’t be shared with any other organization.