### Start a Scan with Detectify API Source: https://context7.com/detectify/api-examples/llms.txt Initiates a new scan for a given scan profile. Requires authentication headers. Handles success (202) and various error codes. ```python import requests from datetime import datetime ENDPOINT = "https://api.detectify.com/rest" def start_scan(scan_profile_token: str): path = f"/v2/scans/{scan_profile_token}/" url = f"{ENDPOINT}{path}" timestamp = str(int(datetime.now().timestamp())) headers = make_headers("POST", path) # make_headers defined in auth section resp = requests.post(url, headers=headers) if resp.status_code == 202: print("Scan started successfully") return True errors = { 400: "Invalid scan profile token", 401: "Missing/invalid API key or signature", 403: "API key cannot access this functionality", 404: "Scan profile not found", 409: "Scan already running", 423: "Domain not verified", } print(f"Error {resp.status_code}: {errors.get(resp.status_code, 'Unknown error')}") return False # Usage scan_profile_token = "5605b488634efe810dff4276e28ca7f9" start_scan(scan_profile_token) # Output: "Scan started successfully" ``` -------------------------------- ### Start a Scan with Detectify API (C#) Source: https://context7.com/detectify/api-examples/llms.txt Initiates a new scan for a given scan profile using HttpClient. Handles success (202) and specific error codes like 409 and 423. ```csharp // C# public async Task StartScanAsync(string scanProfile) { var path = $"/v2/scans/{scanProfile}/"; var url = $"{Endpoint}{path}"; var headers = MakeHeaders("POST", path, DateTime.UtcNow, null); using var client = new HttpClient(); headers.ToList().ForEach(h => client.DefaultRequestHeaders.Add(h.Key, h.Value)); var response = await client.PostAsync(url, null); return (int)response.StatusCode switch { 202 => true, // "Scan start request accepted" 409 => false, // "A scan is already running on the specified profile" 423 => false, // "The domain is not verified" _ => false }; } // Usage var detectify = new Detectify("d4bf676ee6146557cbf0f28fe6cbc290", "SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ=="); bool started = await detectify.StartScanAsync("5605b488634efe810dff4276e28ca7f9"); ``` -------------------------------- ### Start a Scan Source: https://context7.com/detectify/api-examples/llms.txt Initiates a new security scan for a given scan profile. This function is available in Python, C#, and Ruby. ```APIDOC ## POST /v2/scans/{scanProfile}/ ### Description Starts a new scan for the specified scan profile. ### Method POST ### Endpoint /v2/scans/{scanProfile}/ ### Parameters #### Path Parameters - **scanProfile** (string) - Required - The unique identifier for the scan profile. ### Request Example (See language-specific examples in the source) ### Response #### Success Response (202) Indicates that the scan start request was accepted. #### Error Responses - **400**: Invalid scan profile token - **401**: Missing/invalid API key or signature - **403**: API key cannot access this functionality - **404**: Scan profile not found - **409**: Scan already running - **423**: Domain not verified ``` -------------------------------- ### POST /v2/scans/{scanProfile}/ — Start a Scan Source: https://context7.com/detectify/api-examples/llms.txt Initiates a scan for a specified scan profile token. The API returns HTTP 202 on success, HTTP 409 if a scan is already running, and HTTP 423 if the domain is not yet verified. ```APIDOC ## POST /v2/scans/{scanProfile}/ — Start a Scan ### Description Initiates a scan for the specified scan profile token. Returns HTTP 202 on success. Returns 409 if a scan is already running, 423 if the domain is not yet verified. ### Method POST ### Endpoint `/v2/scans/{scanProfile}/` ### Parameters #### Path Parameters - **scanProfile** (string) - Required - The token of the scan profile to initiate a scan for. ``` -------------------------------- ### Start a Scan with Detectify API (Ruby) Source: https://context7.com/detectify/api-examples/llms.txt Initiates a new scan for a given scan profile using the RestClient gem. Handles success (202) and specific error codes like 409 and 423. ```ruby # Ruby require 'rest-client' require 'openssl' require 'Base64' module Detectify ENDPOINT = 'https://api.detectify.com/rest'.freeze def self.start_scan(scan_profile, api_key, secret_key) path = "/v2/scans/#{scan_profile}/" headers = create_headers('POST', path, api_key, secret_key) RestClient.post("#{ENDPOINT}#{path}", '', headers) do |response, _, _| case response.code when 202 then puts 'Scan start request accepted'; true when 409 then puts 'A scan is already running'; false when 423 then puts 'Domain not verified'; false else false end end end end Detectify.start_scan('5605b488634efe810dff4276e28ca7f9', 'd4bf676ee6146557cbf0f28fe6cbc290', 'SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ==') ``` -------------------------------- ### GET /v2/domains/{domainToken}/subdomains/ — Get Subdomains Source: https://context7.com/detectify/api-examples/llms.txt Retrieves all discovered subdomains for a given domain. This is useful for populating a CSV file for bulk scan profile creation. ```APIDOC ## GET /v2/domains/{domainToken}/subdomains/ — Get Subdomains ### Description Returns all discovered subdomains for a given domain. Useful for populating `profiles.csv` for bulk scan profile creation. ### Method GET ### Endpoint /v2/domains/{domainToken}/subdomains/ ### Parameters #### Path Parameters - **domainToken** (string) - Required - The unique token of the domain. ### Request Example ```python # Python 3 — export all subdomains to CSV import requests, csv, json def export_all_subdomains(output_file="all_subdomains.csv"): domains = get_domains() # Assuming get_domains() is defined elsewhere all_subs = [] for domain in domains: path = f"/v2/domains/{domain['token']}/subdomains/" resp = requests.get(f"{ENDPOINT}{path}", headers=make_headers("GET", path)) # Assuming ENDPOINT and make_headers are defined elsewhere if resp.status_code == 200: subs = resp.json() all_subs.extend([s["name"] for s in subs]) print(f"{domain['name']}: {len(subs)} subdomains found") with open(output_file, "w", newline="") as f: writer = csv.writer(f) writer.writerows([[s] for s in all_subs]) print(f"Exported {len(all_subs)} subdomains to {output_file}") export_all_subdomains() ``` ### Response #### Success Response (200) - **name** (string) - The name of the subdomain. #### Response Example ```json [ { "name": "sub.example.com" } ] ``` ``` -------------------------------- ### Get Profiles for a Domain using Python (requests) Source: https://context7.com/detectify/api-examples/llms.txt Retrieves scan profiles associated with a specific domain token. Requires the domain token. ```python # Python 3 import requests def get_domain_profiles(domain_token: str) -> list: path = f"/v2/profiles/{domain_token}/" url = f"{ENDPOINT}{path}" headers = make_headers("GET", path) resp = requests.get(url, headers=headers) resp.raise_for_status() return resp.json() profiles = get_domain_profiles("abc123domaintoken") # Returns list of scan profile objects for the specified domain ``` -------------------------------- ### Get Profiles for a Domain Source: https://context7.com/detectify/api-examples/llms.txt Returns scan profiles associated with a specific domain token. ```APIDOC ## GET /v2/profiles/{domainToken}/ — Get Profiles for a Domain ### Description Returns scan profiles associated with a specific domain token. ### Method GET ### Endpoint /v2/profiles/{domainToken}/ ### Parameters #### Path Parameters - **domainToken** (string) - Required - The unique token of the domain for which to retrieve scan profiles. ### Request Example ```python # Python 3 import requests def get_domain_profiles(domain_token: str) -> list: path = f"/v2/profiles/{domain_token}/" url = f"{ENDPOINT}{path}" headers = make_headers("GET", path) resp = requests.get(url, headers=headers) resp.raise_for_status() return resp.json() profiles = get_domain_profiles("abc123domaintoken") # Returns list of scan profile objects for the specified domain ``` ### Response #### Success Response (200) - Returns a list of scan profile objects associated with the specified domain token. ``` -------------------------------- ### Get Latest Full Report Source: https://context7.com/detectify/api-examples/llms.txt Retrieves the full vulnerability report from the most recent completed scan for a specified profile. ```APIDOC ## GET /v2/fullreports/{scanProfile}/latest/ — Get Latest Full Report ### Description Returns the full vulnerability report from the most recent completed scan for a profile. ### Method GET ### Endpoint /v2/fullreports/{scanProfile}/latest/ ### Parameters #### Path Parameters - **scanProfile** (string) - Required - The unique token of the scan profile. ``` -------------------------------- ### GET /v2/findings/{scanProfile}/ — Get Scan Findings Source: https://context7.com/detectify/api-examples/llms.txt Retrieves vulnerability findings for a specific scan profile. Findings can be filtered by severity and time range. ```APIDOC ## GET /v2/findings/{scanProfile}/ — Get Scan Findings ### Description Returns vulnerability findings for a scan profile filtered by severity and time range. ### Method GET ### Endpoint /v2/findings/{scanProfile}/ ### Parameters #### Path Parameters - **scanProfile** (string) - Required - The token of the scan profile. #### Query Parameters - **severity** (string) - Optional - Filter findings by severity (e.g., `high`, `medium`, `low`). - **from** (integer) - Optional - Unix timestamp for the start of the time range. - **to** (integer) - Optional - Unix timestamp for the end of the time range. ### Request Example ```python # Python 3 — get high-severity findings for the last 100 days import requests, time time_interval = 100 # days def get_findings_for_scan_profile(scan_profile_token: str) -> list: since = int(time.time() - time_interval * 86400) now = int(time.time()) path = f"/v2/findings/{scan_profile_token}/?severity=high&from={since}&to={now}" resp = requests.get(f"{ENDPOINT}{path}", headers=make_headers("GET", path)) # Assuming ENDPOINT and make_headers are defined elsewhere if resp.status_code == 200: findings = resp.json() print(f"Found {len(findings)} high-severity findings") return findings return [] findings = get_findings_for_scan_profile("5605b488634efe810dff4276e28ca7f9") ``` ### Response #### Success Response (200) - **severity** (string) - The severity level of the finding. #### Response Example ```json [ { "severity": "high" } ] ``` ``` -------------------------------- ### GET /v2/domains/{domainToken}/findings/ — Get DMS Findings Source: https://context7.com/detectify/api-examples/llms.txt Retrieves Domain Monitoring Service (DMS) findings for a domain. Findings can be filtered by severity and time range. Requires a DMS-enabled Enterprise license. ```APIDOC ## GET /v2/domains/{domainToken}/findings/ — Get DMS Findings ### Description Returns Domain Monitoring Service (DMS) findings for a domain, filterable by severity and time range. Requires a DMS-enabled Enterprise license. ### Method GET ### Endpoint /v2/domains/{domainToken}/findings/ ### Parameters #### Path Parameters - **domainToken** (string) - Required - The unique token of the domain. #### Query Parameters - **severity** (string) - Optional - Filter findings by severity (e.g., `high`, `medium`, `low`). - **from** (integer) - Optional - Unix timestamp for the start of the time range. - **to** (integer) - Optional - Unix timestamp for the end of the time range. ### Request Example ```python # Python 3 — export high-severity DMS findings to CSV import requests, json, time import pandas as pd def get_dms_findings(output_prefix="dms_findings"): domains = get_domains() # Assuming get_domains() is defined elsewhere unpatched, patched = [], [] for domain in domains: path = f"/v2/domains/{domain['token']}/findings/?severity=high&from={int(time.time()-30*86400)}&to={int(time.time())}" resp = requests.get(f"{ENDPOINT}{path}", headers=make_headers("GET", path)) # Assuming ENDPOINT and make_headers are defined elsewhere if resp.status_code != 200: continue for finding in resp.json(): tags = finding.get("tags", []) if any(t.get("value") == "patched" for t in tags): patched.append(finding) else: unpatched.append(finding) print(f"Unpatched high-severity findings: {len(unpatched)}") print(f"Patched high-severity findings: {len(patched)}") if unpatched: pd.json_normalize(unpatched).to_csv(f"{output_prefix}_unpatched.csv", index=False) if patched: pd.json_normalize(patched).to_csv(f"{output_prefix}_patched.csv", index=False) get_dms_findings() ``` ### Response #### Success Response (200) - **tags** (array) - List of tags associated with the finding. - **severity** (string) - The severity level of the finding. #### Response Example ```json [ { "tags": [ {"value": "unpatched"} ], "severity": "high" } ] ``` ``` -------------------------------- ### Get Scan Status Source: https://context7.com/detectify/api-examples/llms.txt Retrieves the current status of a running scan for a specific profile. Supported in Python and Node.js. ```APIDOC ## GET /v2/scans/{scanProfile}/ ### Description Returns a JSON object with the current running scan status for the specified profile. ### Method GET ### Endpoint /v2/scans/{scanProfile}/ ### Parameters #### Path Parameters - **scanProfile** (string) - Required - The unique identifier for the scan profile. ### Request Example (See language-specific examples in the source) ### Response #### Success Response (200) - **status** (string) - The current status of the scan (e.g., "running"). - **phase** (string) - The current phase of the scan (e.g., "crawling"). - **profile_token** (string) - The token of the scan profile. - **started** (integer) - The timestamp when the scan started. #### Error Responses - **404**: No scan running for this profile ``` -------------------------------- ### Get Latest Full Vulnerability Report Source: https://context7.com/detectify/api-examples/llms.txt Retrieves the most recent full vulnerability report for a given scan profile. Handles cases where no completed scans are found for the profile. ```python import requests def get_latest_full_report(scan_profile_token: str) -> dict | None: path = f"/v2/fullreports/{scan_profile_token}/latest/" resp = requests.get(f"{ENDPOINT}{path}", headers=make_headers("GET", path)) if resp.status_code == 200: report = resp.json() print(f"Report from scan completed at: {report.get('created')}") print(f"Total findings: {len(report.get('findings', []))}") return report if resp.status_code == 404: print("No completed scans found for this profile") return None report = get_latest_full_report("5605b488634efe810dff4276e28ca7f9") ``` -------------------------------- ### Get Scan Status with Detectify API Source: https://context7.com/detectify/api-examples/llms.txt Retrieves the current status of a running scan for a specified profile. Returns scan data on success (200) or indicates no scan is running (404). ```python # Python 3 import requests def scan_status(scan_profile_token: str) -> dict | None: path = f"/v2/scans/{scan_profile_token}/" url = f"{ENDPOINT}{path}" headers = make_headers("GET", path) resp = requests.get(url, headers=headers) if resp.status_code == 200: data = resp.json() print(data) return data if resp.status_code == 404: print("No scan running for this profile") return None # Example output for a running scan: # {"status": "running", "phase": "crawling", "profile_token": "5605b488...", "started": 1700000000} status = scan_status("5605b488634efe810dff4276e28ca7f9") ``` -------------------------------- ### Get Scan Status with Detectify API (Node.js) Source: https://context7.com/detectify/api-examples/llms.txt Retrieves the current status of a running scan for a specified profile using node-fetch. Handles success (200) and indicates no scan is running (404). ```javascript // Node.js const fetch = require('node-fetch') const DetectifyEndpoint = 'https://api.detectify.com/rest' function scanStatus(scanProfile, apiKey, secretKey) { const path = `/v2/scans/${scanProfile}/` const url = `${DetectifyEndpoint}${path}` const timestamp = Math.floor(Date.now() / 1000) const headers = makeHeaders(apiKey, secretKey, 'GET', path, timestamp) fetch(url, { method: 'GET', headers }) .then(response => { if (response.status === 200) return response.json() if (response.status === 404) console.log('No scan running') return null }) .then(data => { if (data) console.log(data) }) } scanStatus('5605b488634efe810dff4276e28ca7f9', 'd4bf676ee6146557cbf0f28fe6cbc290', 'SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ==') ``` -------------------------------- ### Python 3 — Get High-Severity Findings for Scan Profile Source: https://context7.com/detectify/api-examples/llms.txt Retrieves high-severity vulnerability findings for a specific scan profile within the last 100 days. Ensure the `scan_profile_token` is correctly provided. ```python # Python 3 — get high-severity findings for the last 100 days import requests, time time_interval = 100 # days def get_findings_for_scan_profile(scan_profile_token: str) -> list: since = int(time.time() - time_interval * 86400) now = int(time.time()) path = f"/v2/findings/{scan_profile_token}/?severity=high&from={since}&to={now}" resp = requests.get(f"{ENDPOINT}{path}", headers=make_headers("GET", path)) if resp.status_code == 200: findings = resp.json() print(f"Found {len(findings)} high-severity findings") return findings return [] findings = get_findings_for_scan_profile("5605b488634efe810dff4276e28ca7f9") # Output: Found 3 high-severity findings ``` -------------------------------- ### Add a Domain using Python (requests) Source: https://context7.com/detectify/api-examples/llms.txt Registers an apex domain with Detectify. Use the apex domain only (no subdomains) to enable auto-discovery. Requires the domain name. ```python # Python 3 import requests, json def add_domain(domain: str): path = "/v2/domains/" url = f"{ENDPOINT}{path}" data = {"name": domain} timestamp = str(int(datetime.now().timestamp())) headers = make_headers("POST", path, json.dumps(data)) headers["Content-Type"] = "application/json" resp = requests.post(url, headers=headers, data=json.dumps(data)) resp.raise_for_status() print(f"Domain added: {resp.json()}") return resp.json() # Add an apex domain (no subdomains) add_domain("example.com") # Output: {"name": "example.com", "token": "abc123...", "verified": false, "created": "2024-01-01T00:00:00Z"} ``` -------------------------------- ### Add a Domain Source: https://context7.com/detectify/api-examples/llms.txt Registers an apex domain with Detectify for ownership verification and subdomain discovery. Use the apex domain only (no subdomains) to enable auto-discovery. ```APIDOC ## POST /v2/domains/ — Add a Domain ### Description Registers an apex domain with Detectify for ownership verification and subdomain discovery. Use the apex domain only (no subdomains) to enable auto-discovery. ### Method POST ### Endpoint /v2/domains/ ### Parameters #### Request Body - **name** (string) - Required - The apex domain name to register. ### Request Example ```python # Python 3 import requests, json def add_domain(domain: str): path = "/v2/domains/" url = f"{ENDPOINT}{path}" data = {"name": domain} timestamp = str(int(datetime.now().timestamp())) headers = make_headers("POST", path, json.dumps(data)) headers["Content-Type"] = "application/json" resp = requests.post(url, headers=headers, data=json.dumps(data)) resp.raise_for_status() print(f"Domain added: {resp.json()}") return resp.json() # Add an apex domain (no subdomains) add_domain("example.com") # Output: {"name": "example.com", "token": "abc123...", "verified": false, "created": "2024-01-01T00:00:00Z"} ``` ### Response #### Success Response (200) - **name** (string) - The registered domain name. - **token** (string) - The unique token associated with the domain. - **verified** (boolean) - Indicates if the domain ownership has been verified. - **created** (string) - The timestamp when the domain was registered. ``` -------------------------------- ### List All Domains using Python (requests) Source: https://context7.com/detectify/api-examples/llms.txt Retrieves a list of all registered domains, including their verification and monitoring status, creation timestamp, and domain token. No parameters are required. ```python # Python 3 import requests def get_domains() -> list: path = "/v2/domains/" url = f"{ENDPOINT}{path}" headers = make_headers("GET", path) resp = requests.get(url, headers=headers) resp.raise_for_status() domains = resp.json() for d in domains: print(f"{d['name']} | verified={d.get('verified')} | token={d['token']}") return domains domains = get_domains() # Output: # example.com | verified=True | token=abc123... # test.com | verified=False | token=def456... ``` -------------------------------- ### List All Domains Source: https://context7.com/detectify/api-examples/llms.txt Returns a JSON array of all domains registered to the team, including verification status, monitoring status, creation timestamp, and domain token. ```APIDOC ## GET /v2/domains/ — List All Domains ### Description Returns a JSON array of all domains registered to the team, including verification status, monitoring status, creation timestamp, and domain token. ### Method GET ### Endpoint /v2/domains/ ### Response #### Success Response (200) - **name** (string) - The registered domain name. - **token** (string) - The unique token associated with the domain. - **verified** (boolean) - Indicates if the domain ownership has been verified. - **created** (string) - The timestamp when the domain was registered. ### Request Example ```python # Python 3 import requests def get_domains() -> list: path = "/v2/domains/" url = f"{ENDPOINT}{path}" headers = make_headers("GET", path) resp = requests.get(url, headers=headers) resp.raise_for_status() domains = resp.json() for d in domains: print(f"{d['name']} | verified={d.get('verified')} | token={d['token']}") return domains domains = get_domains() # Output: # example.com | verified=True | token=abc123... # test.com | verified=False | token=def456... ``` ``` -------------------------------- ### List All Scan Profiles using Python (requests) Source: https://context7.com/detectify/api-examples/llms.txt Retrieves all scan profiles for the team, including endpoint, status, and token. No parameters are required. ```python # Python 3 import requests def get_all_profiles() -> list: path = "/v2/profiles/" url = f"{ENDPOINT}{path}" headers = make_headers("GET", path) resp = requests.get(url, headers=headers) resp.raise_for_status() profiles = resp.json() verified = sum(1 for p in profiles if p.get("status") == "verified") print(f"Total profiles: {len(profiles)} | Verified: {verified} | Unverified: {len(profiles)-verified}") return profiles profiles = get_all_profiles() # Output: Total profiles: 42 | Verified: 38 | Unverified: 4 ``` -------------------------------- ### Generate HMAC-SHA256 Signature for Detectify API (curl) Source: https://context7.com/detectify/api-examples/llms.txt Illustrates manual HMAC-SHA256 signing for Detectify API requests using curl. Not recommended for production; use a script for security. ```bash # curl — manual HMAC signing (illustrative; use a script for production) TIMESTAMP=$(date +%s) API_KEY="d4bf676ee6146557cbf0f28fe6cbc290" PATH="/v2/profiles/" MSG="GET;${PATH};${API_KEY};${TIMESTAMP};" SECRET=$(echo "SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ==" | base64 -d | xxd -p) SIG=$(echo -n "$MSG" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$SECRET" -binary | base64) curl -H "X-Detectify-Key: $API_KEY" \ -H "X-Detectify-Signature: $SIG" \ -H "X-Detectify-Timestamp: $TIMESTAMP" \ "https://api.detectify.com/rest${PATH}" ``` -------------------------------- ### Python 3 — Export All Subdomains to CSV Source: https://context7.com/detectify/api-examples/llms.txt Retrieves all discovered subdomains for a given domain and exports them to a CSV file. Useful for populating scan profile creation data. ```python # Python 3 — export all subdomains to CSV import requests, csv, json def export_all_subdomains(output_file="all_subdomains.csv"): domains = get_domains() all_subs = [] for domain in domains: path = f"/v2/domains/{domain['token']}/subdomains/" resp = requests.get(f"{ENDPOINT}{path}", headers=make_headers("GET", path)) if resp.status_code == 200: subs = resp.json() all_subs.extend([s["name"] for s in subs]) print(f"{domain['name']}: {len(subs)} subdomains found") with open(output_file, "w", newline="") as f: writer = csv.writer(f) writer.writerows([[s] for s in all_subs]) print(f"Exported {len(all_subs)} subdomains to {output_file}") export_all_subdomains() # Output: # example.com: 15 subdomains found # test.com: 3 subdomains found # Exported 18 subdomains to all_subdomains.csv ``` -------------------------------- ### Add a Scan Profile using Python (requests) Source: https://context7.com/detectify/api-examples/llms.txt Creates a new scan profile for a specific endpoint (domain, subdomain, or IP). Setting `unique: true` prevents duplicate profiles. Requires the endpoint to scan. ```python # Python 3 import requests, json def add_scan_profile(scan_profile: str) -> str | None: path = "/v2/profiles/" url = f"{ENDPOINT}{path}" data = {"endpoint": scan_profile, "unique": True} headers = make_headers("POST", path, json.dumps(data)) headers["Content-Type"] = "application/json" resp = requests.post(url, headers=headers, data=json.dumps(data)) resp.raise_for_status() token = resp.json().get("token") print(f"Scan profile created, token: {token}") return token # Add a subdomain as a scan profile token = add_scan_profile("app.example.com") # Output: "Scan profile created, token: 5605b488634efe810dff4276e28ca7f9" ``` -------------------------------- ### Generate HMAC-SHA256 Signature for Detectify API (Python) Source: https://context7.com/detectify/api-examples/llms.txt Generates the necessary headers for authenticating with the Detectify API using HMAC-SHA256. Ensure API_KEY and API_SECRET_KEY environment variables are set. ```python # Python 3 — signature generation import hmac, hashlib, os from base64 import b64encode, b64decode from datetime import datetime api_key = os.getenv("API_KEY") api_secret_key = os.getenv("API_SECRET_KEY") def make_signature(method: str, path: str, timestamp: str, body: str = None) -> str: msg = f"{method.upper()};{path};{api_key};{timestamp};" if body: msg += body secret = b64decode(api_secret_key) digest = hmac.new(secret, msg=bytes(msg, "utf-8"), digestmod=hashlib.sha256).digest() return b64encode(digest).decode("utf-8") def make_headers(method: str, path: str, body: str = None) -> dict: timestamp = str(int(datetime.now().timestamp())) return { "X-Detectify-Key": api_key, "X-Detectify-Signature": make_signature(method, path, timestamp, body), "X-Detectify-Timestamp": timestamp, } # Example: headers for GET /v2/profiles/ headers = make_headers("GET", "/v2/profiles/") # {"X-Detectify-Key": "...", "X-Detectify-Signature": "...", "X-Detectify-Timestamp": "1700000000"} ``` -------------------------------- ### List All Scan Profiles Source: https://context7.com/detectify/api-examples/llms.txt Returns all scan profiles for the team, including endpoint, status (verified/unverified), and token. ```APIDOC ## GET /v2/profiles/ — List All Scan Profiles ### Description Returns all scan profiles for the team, including endpoint, status (verified/unverified), and token. ### Method GET ### Endpoint /v2/profiles/ ### Response #### Success Response (200) - **endpoint** (string) - The endpoint (domain, subdomain, or IP) for the scan profile. - **status** (string) - The verification status of the scan profile (e.g., 'verified', 'unverified'). - **token** (string) - The unique token for the scan profile. ### Request Example ```python # Python 3 import requests def get_all_profiles() -> list: path = "/v2/profiles/" url = f"{ENDPOINT}{path}" headers = make_headers("GET", path) resp = requests.get(url, headers=headers) resp.raise_for_status() profiles = resp.json() verified = sum(1 for p in profiles if p.get("status") == "verified") print(f"Total profiles: {len(profiles)} | Verified: {verified} | Unverified: {len(profiles)-verified}") return profiles profiles = get_all_profiles() # Output: Total profiles: 42 | Verified: 38 | Unverified: 4 ``` ``` -------------------------------- ### Generate HMAC-SHA256 Signature for Detectify API (Node.js) Source: https://context7.com/detectify/api-examples/llms.txt Generates the necessary headers for authenticating with the Detectify API using HMAC-SHA256. Requires the 'crypto' module. ```javascript // Node.js — signature generation const crypto = require('crypto') function makeHeaders(apiKey, secretKey, method, path, timestamp) { const data = `${method.toUpperCase()};${path};${apiKey};${timestamp};` const secret = Buffer.from(secretKey, 'base64') const hmac = crypto.createHmac('sha256', secret) hmac.update(data) const signature = hmac.digest('base64') return { 'X-Detectify-Key': apiKey, 'X-Detectify-Signature': signature, 'X-Detectify-Timestamp': timestamp, } } const apiKey = 'd4bf676ee6146557cbf0f28fe6cbc290' const secretKey = 'SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ==' const timestamp = Math.floor(Date.now() / 1000) const headers = makeHeaders(apiKey, secretKey, 'GET', '/v2/profiles/', timestamp) ``` -------------------------------- ### Bulk Operations with apikeys.csv Source: https://context7.com/detectify/api-examples/llms.txt Reads team credentials from 'apikeys.csv' (semicolon-delimited) to apply operations across multiple teams. This script demonstrates iterating through teams and processing their scan profiles. ```python # Python 2.7 enterprise script — apikeys.csv format: # TeamName;APIKey # TeamA;d4bf676ee6146557cbf0f28fe6cbc290 # TeamB;a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 # Reading API keys and iterating teams import csv, json with open('apikeys.csv', 'rU') as csvfile: next(csvfile) # skip header reader = csv.reader(csvfile, delimiter=';') for row in reader: team_name = row[0] api_key = row[1] print(f"Processing team: {team_name}") # Get all scan profiles for this team all_profiles = get_all_profiles(api_key, secret_key) verified = sum(1 for p in all_profiles if p["status"] == "verified") unverified = len(all_profiles) - verified print(f" Profiles: {len(all_profiles)} total, {verified} verified, {unverified} unverified") ``` ```python # Bulk-add profiles from profiles.csv (one endpoint per line) def add_scan_profile_csv(api_key, secret_key): path = "/profiles/" with open('profiles.csv', 'r') as f: reader = csv.reader(f, delimiter=' ') for row in reader: endpoint = row[0] req = request(api_key, secret_key, path, 'POST', { 'endpoint': endpoint, 'unique': True }) if req is not None: print(f"Added: {endpoint}") ``` -------------------------------- ### Bulk Operations via apikeys.csv (Enterprise) Source: https://context7.com/detectify/api-examples/llms.txt This section describes enterprise-level bulk operations using a Python script that reads team credentials from `apikeys.csv` to manage profiles, domains, and schedules across multiple teams. ```APIDOC ## Bulk Operations via apikeys.csv (Enterprise) ### Description The enterprise Python script reads team credentials from `apikeys.csv` (semicolon-delimited: `TeamName;APIKey`) and applies operations across multiple teams. This pattern enables large-scale management of profiles, domains, and schedules. ### Reading API keys and iterating teams ```python # Python 2.7 enterprise script — apikeys.csv format: # TeamName;APIKey # TeamA;d4bf676ee6146557cbf0f28fe6cbc290 # TeamB;a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 # Reading API keys and iterating teams import csv, json with open('apikeys.csv', 'rU') as csvfile: next(csvfile) # skip header reader = csv.reader(csvfile, delimiter=';') for row in reader: team_name = row[0] api_key = row[1] print(f"Processing team: {team_name}") # Get all scan profiles for this team all_profiles = get_all_profiles(api_key, secret_key) verified = sum(1 for p in all_profiles if p["status"] == "verified") unverified = len(all_profiles) - verified print(f" Profiles: {len(all_profiles)} total, {verified} verified, {unverified} unverified") ``` ### Bulk-add profiles from profiles.csv ```python # Bulk-add profiles from profiles.csv (one endpoint per line) def add_scan_profile_csv(api_key, secret_key): path = "/profiles/" with open('profiles.csv', 'r') as f: reader = csv.reader(f, delimiter=' ') for row in reader: endpoint = row[0] req = request(api_key, secret_key, path, 'POST', { 'endpoint': endpoint, 'unique': True }) if req is not None: print(f"Added: {endpoint}") ``` ``` -------------------------------- ### Authentication: HMAC-SHA256 Request Signing Source: https://context7.com/detectify/api-examples/llms.txt Demonstrates how to generate HMAC-SHA256 signatures for API requests, which is required for authentication. ```APIDOC ## Authentication: HMAC-SHA256 Request Signing Every Detectify API request must include three headers: `X-Detectify-Key` (your API key), `X-Detectify-Signature` (an HMAC-SHA256 signature), and `X-Detectify-Timestamp` (Unix epoch time). The signature is computed by HMAC-SHA256-signing the string `METHOD;PATH;API_KEY;TIMESTAMP;BODY` with the base64-decoded secret key, then base64-encoding the result. ### Python 3 Example ```python import hmac, hashlib, os from base64 import b64encode, b64decode from datetime import datetime api_key = os.getenv("API_KEY") api_secret_key = os.getenv("API_SECRET_KEY") def make_signature(method: str, path: str, timestamp: str, body: str = None) -> str: msg = f"{method.upper()};{path};{api_key};{timestamp};" if body: msg += body secret = b64decode(api_secret_key) digest = hmac.new(secret, msg=bytes(msg, "utf-8"), digestmod=hashlib.sha256).digest() return b64encode(digest).decode("utf-8") def make_headers(method: str, path: str, body: str = None) -> dict: timestamp = str(int(datetime.now().timestamp())) return { "X-Detectify-Key": api_key, "X-Detectify-Signature": make_signature(method, path, timestamp, body), "X-Detectify-Timestamp": timestamp, } # Example: headers for GET /v2/profiles/ headers = make_headers("GET", "/v2/profiles/") # {"X-Detectify-Key": "...", "X-Detectify-Signature": "...", "X-Detectify-Timestamp": "1700000000"} ``` ### Node.js Example ```javascript const crypto = require('crypto') function makeHeaders(apiKey, secretKey, method, path, timestamp) { const data = `${method.toUpperCase()};${path};${apiKey};${timestamp};` const secret = Buffer.from(secretKey, 'base64') const hmac = crypto.createHmac('sha256', secret) hmac.update(data) const signature = hmac.digest('base64') return { 'X-Detectify-Key': apiKey, 'X-Detectify-Signature': signature, 'X-Detectify-Timestamp': timestamp, } } const apiKey = 'd4bf676ee6146557cbf0f28fe6cbc290' const secretKey = 'SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ==' const timestamp = Math.floor(Date.now() / 1000) const headers = makeHeaders(apiKey, secretKey, 'GET', '/v2/profiles/', timestamp) ``` ### cURL Example ```bash TIMESTAMP=$(date +%s) API_KEY="d4bf676ee6146557cbf0f28fe6cbc290" PATH="/v2/profiles/" MSG="GET;${PATH};${API_KEY};${TIMESTAMP};" SECRET=$(echo "SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ==" | base64 -d | xxd -p) SIG=$(echo -n "$MSG" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$SECRET" -binary | base64) curl -H "X-Detectify-Key: $API_KEY" \ -H "X-Detectify-Signature: $SIG" \ -H "X-Detectify-Timestamp: $TIMESTAMP" \ "https://api.detectify.com/rest${PATH}" ``` ``` -------------------------------- ### Add a Scan Profile Source: https://context7.com/detectify/api-examples/llms.txt Creates a new scan profile for a specific endpoint (domain, subdomain, or IP). Setting `unique: true` prevents duplicate profiles. ```APIDOC ## POST /v2/profiles/ — Add a Scan Profile ### Description Creates a new scan profile for a specific endpoint (domain, subdomain, or IP). Setting `unique: true` prevents duplicate profiles. ### Method POST ### Endpoint /v2/profiles/ ### Parameters #### Request Body - **endpoint** (string) - Required - The domain, subdomain, or IP address to scan. - **unique** (boolean) - Optional - If true, prevents duplicate scan profiles for the same endpoint. ### Request Example ```python # Python 3 import requests, json def add_scan_profile(scan_profile: str) -> str | None: path = "/v2/profiles/" url = f"{ENDPOINT}{path}" data = {"endpoint": scan_profile, "unique": True} headers = make_headers("POST", path, json.dumps(data)) headers["Content-Type"] = "application/json" resp = requests.post(url, headers=headers, data=json.dumps(data)) resp.raise_for_status() token = resp.json().get("token") print(f"Scan profile created, token: {token}") return token # Add a subdomain as a scan profile token = add_scan_profile("app.example.com") # Output: "Scan profile created, token: 5605b488634efe810dff4276e28ca7f9" ``` ### Response #### Success Response (200) - **token** (string) - The unique token for the newly created scan profile. ``` -------------------------------- ### Update Scan Schedules for All Profiles Source: https://context7.com/detectify/api-examples/llms.txt Sets a weekly scan schedule for all profiles in a team. Ensure the frequency parameter is one of the valid options: 'once', 'daily', 'weekly', 'biweekly', 'monthly'. ```python import requests, json def update_all_scan_schedules(frequency: str = "weekly"): valid = {"once", "daily", "weekly", "biweekly", "monthly"} if frequency not in valid: raise ValueError(f"frequency must be one of {valid}") profiles = get_all_profiles() for profile in profiles: path = f"/v2/scanschedules/{profile['token']}/" data = {"frequency": frequency} headers = make_headers("POST", path, json.dumps(data)) headers["Content-Type"] = "application/json" resp = requests.post(f"{ENDPOINT}{path}", headers=headers, data=json.dumps(data)) status = "updated" if resp.status_code == 200 else f"failed ({resp.status_code})" print(f"{profile['endpoint']}: schedule {status} → {frequency}") update_all_scan_schedules("weekly") ``` -------------------------------- ### Python 3 — Bulk Delete All Scan Profiles Source: https://context7.com/detectify/api-examples/llms.txt Use this script with extreme caution as it permanently deletes all scan profiles. It prompts for confirmation before proceeding. ```python import requests def delete_all_scan_profiles(): profiles = get_all_profiles() if not profiles: print("No scan profiles to delete") return confirm = input(f"Confirm DELETE of {len(profiles)} profiles? [Y/n]: ").lower() if confirm != 'y': print("Aborted.") return for profile in profiles: path = f"/v2/profiles/{profile['token']}/" url = f"{ENDPOINT}{path}" headers = make_headers("DELETE", path) resp = requests.delete(url, headers=headers) if resp.status_code == 200: print(f"Deleted: {profile['endpoint']}") else: print(f"Failed to delete {profile['endpoint']}: {resp.status_code}") ```