{"openapi":"3.0.3","info":{"title":"FortifyNet API","version":"1","description":"Read your organizations, domains, audit reports, uptime and alerts, and start audits.\n\nAuthentication: send your API key in the X-API-Key header. Keys are created under Settings in the app.\nA key acts as your account: it reaches the organizations you belong to, where the organization owner is on Professional or Enterprise.\n\nLimits: 120 requests a minute per key, 30 audits an hour per key and 300 audits a day per account.\nAnswers are JSON: { \"data\": ... } on success, { \"error\": { \"code\", \"message\" } } otherwise. Times are UTC in ISO 8601 form.\nThe API is meant for servers. It sends no CORS headers, so it cannot be called from web pages on other sites."},"servers":[{"url":"https://fortifynet.com/functions"}],"security":[{"ApiKey":[]}],"paths":{"/api-v1-me":{"get":{"operationId":"getMe","summary":"The key in use","tags":["Account"],"description":"Returns the account and the key, its limits, and the address the call came from as the IP allowlist sees it. Useful as a first test of a key.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Me"}}}}}},"401":{"description":"The key is missing, wrong, revoked or expired (missing_api_key, invalid_api_key, api_key_revoked, api_key_expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not do this: plan_required, ip_not_allowed, missing_scope or forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (rate_limited). The Retry-After header says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-v1-organizations":{"get":{"operationId":"listOrganizations","summary":"Organizations the key reaches","tags":["Organizations"],"description":"Every organization the account belongs to. api_access tells whether the API is open there (the plan of the organization owner).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Organization"}},"meta":{"type":"object","properties":{"count":{"type":"integer"}}}}}}}},"401":{"description":"The key is missing, wrong, revoked or expired (missing_api_key, invalid_api_key, api_key_revoked, api_key_expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not do this: plan_required, ip_not_allowed, missing_scope or forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (rate_limited). The Retry-After header says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-v1-domains":{"get":{"operationId":"listDomains","summary":"Domains","tags":["Domains"],"description":"With \"id\": one domain, returned as an object in data. Otherwise a list, sorted by name, from one organization or from all organizations the key reaches.","parameters":[{"name":"id","in":"query","required":false,"description":"Return this domain only.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"organization_id","in":"query","required":false,"description":"Only domains of this organization.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"domain","in":"query","description":"Only the domain with exactly this name, for example example.com.","schema":{"type":"string"}},{"name":"tag","in":"query","description":"Only domains carrying this tag.","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Domain"}}},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Domain"}},"meta":{"type":"object","properties":{"count":{"type":"integer"},"has_more":{"type":"boolean"},"next_offset":{"type":"integer","nullable":true}}}}}]}}}},"400":{"description":"A parameter is not valid (invalid_parameter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The key is missing, wrong, revoked or expired (missing_api_key, invalid_api_key, api_key_revoked, api_key_expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not do this: plan_required, ip_not_allowed, missing_scope or forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such domain or organization for this key (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (rate_limited). The Retry-After header says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-v1-reports":{"get":{"operationId":"listReports","summary":"Audit reports","tags":["Reports"],"description":"With \"id\": one report with its checks and findings. Otherwise a list, newest first, without findings. Page backwards by passing meta.next_before as \"before\".","parameters":[{"name":"id","in":"query","required":false,"description":"Return this report with its findings.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"domain_id","in":"query","required":false,"description":"Only reports of this domain.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"organization_id","in":"query","required":false,"description":"Only reports of this organization.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","running","completed","failed"]}},{"name":"before","in":"query","description":"Only reports created before this time.","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Report"}}},{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ReportSummary"}},"meta":{"type":"object","properties":{"count":{"type":"integer"},"has_more":{"type":"boolean"},"next_before":{"type":"string","nullable":true,"format":"date-time"}}}}}]}}}},"400":{"description":"A parameter is not valid (invalid_parameter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The key is missing, wrong, revoked or expired (missing_api_key, invalid_api_key, api_key_revoked, api_key_expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not do this: plan_required, ip_not_allowed, missing_scope or forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such report or domain for this key (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (rate_limited). The Retry-After header says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-v1-uptime":{"get":{"operationId":"getUptime","summary":"Uptime of a domain","tags":["Uptime"],"description":"Availability over the last hours (default 24, at most 2160 = 90 days), outages, and the stored checks. A check is stored when the state changes and otherwise about once an hour; availability is weighted by time, and time without a known state counts as \"no data\".","parameters":[{"name":"domain_id","in":"query","required":true,"description":"The domain.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"hours","in":"query","schema":{"type":"integer","minimum":1,"maximum":2160,"default":24}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Uptime"}}}}}},"400":{"description":"A parameter is not valid (invalid_parameter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The key is missing, wrong, revoked or expired (missing_api_key, invalid_api_key, api_key_revoked, api_key_expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not do this: plan_required, ip_not_allowed, missing_scope or forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such domain for this key (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (rate_limited). The Retry-After header says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-v1-alerts":{"get":{"operationId":"listAlerts","summary":"Monitoring alerts","tags":["Alerts"],"description":"Alerts raised by monitoring, newest first: changes in SSL, DNS and headers, new findings, score drops, downtime, paused and resumed monitoring.","parameters":[{"name":"domain_id","in":"query","required":false,"description":"Only alerts of this domain.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"organization_id","in":"query","required":false,"description":"Only alerts of this organization.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$"}},{"name":"since","in":"query","description":"Only alerts created at or after this time.","schema":{"type":"string","format":"date-time"}},{"name":"before","in":"query","description":"Only alerts created before this time.","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}},"meta":{"type":"object","properties":{"count":{"type":"integer"},"has_more":{"type":"boolean"},"next_before":{"type":"string","nullable":true,"format":"date-time"}}}}}}}},"400":{"description":"A parameter is not valid (invalid_parameter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The key is missing, wrong, revoked or expired (missing_api_key, invalid_api_key, api_key_revoked, api_key_expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not do this: plan_required, ip_not_allowed, missing_scope or forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such domain or organization for this key (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (rate_limited). The Retry-After header says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-v1-audits":{"post":{"operationId":"startAudit","summary":"Start an audit","tags":["Audits"],"description":"Runs an audit of a domain and returns the finished report. The key needs the \"audit\" permission and the account must be owner or admin in the domain's organization. The call answers when the audit is finished, normally within a minute; allow two. If the connection is cut before that, the audit still completes: read the newest report of the domain with api-v1-reports. Every test the plan of the organization owner includes is run; the result becomes the domain's current score.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain_id"],"properties":{"domain_id":{"type":"string","pattern":"^[a-f0-9]{24}$"},"lang":{"type":"string","enum":["en","sv","de","es","fi"],"default":"en","description":"Language of the findings."}}}}}},"responses":{"201":{"description":"The audit ran; the report is returned.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Report"}}}}}},"400":{"description":"The body or a field is not valid (invalid_body, invalid_parameter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The key is missing, wrong, revoked or expired (missing_api_key, invalid_api_key, api_key_revoked, api_key_expired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key may not do this: plan_required, ip_not_allowed, missing_scope or forbidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such domain for this key (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The domain name does not exist in DNS (domain_not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (rate_limited). The Retry-After header says how many seconds to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The audit could not be completed (audit_failed). error.report_id names the failed report.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-v1-openapi":{"get":{"operationId":"getOpenapi","summary":"This document","tags":["Account"],"security":[],"responses":{"200":{"description":"OpenAPI 3.0 document","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}},"additionalProperties":true}}},"Me":{"type":"object","properties":{"account":{"type":"string"},"key":{"type":"object","properties":{"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","audit"]}},"organization_id":{"type":"string","nullable":true},"allowed_ips":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","nullable":true,"format":"date-time"},"expires_at":{"type":"string","nullable":true,"format":"date-time"}}},"caller_address":{"type":"string","nullable":true},"limits":{"type":"object","properties":{"requests_per_minute":{"type":"integer"},"audits_per_hour":{"type":"integer"},"audits_per_day":{"type":"integer"}}},"organizations":{"type":"object","properties":{"total":{"type":"integer"},"with_api_access":{"type":"integer"}}},"api_version":{"type":"string"}}},"Organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["personal","client"]},"role":{"type":"string","enum":["owner","admin","viewer"]},"api_access":{"type":"boolean"}}},"Domain":{"type":"object","properties":{"id":{"type":"string"},"organization_id":{"type":"string","nullable":true},"domain":{"type":"string"},"verified":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"notes":{"type":"string"},"created_at":{"type":"string","nullable":true,"format":"date-time"},"scores":{"type":"object","properties":{"overall":{"type":"integer","nullable":true,"minimum":0,"maximum":100},"ssl":{"type":"integer","nullable":true,"minimum":0,"maximum":100},"dns":{"type":"integer","nullable":true,"minimum":0,"maximum":100},"performance":{"type":"integer","nullable":true,"minimum":0,"maximum":100},"security_headers":{"type":"integer","nullable":true,"minimum":0,"maximum":100},"dark_web":{"type":"integer","nullable":true,"minimum":0,"maximum":100}}},"last_audit_at":{"type":"string","nullable":true,"format":"date-time"},"monitoring":{"type":"object","properties":{"enabled":{"type":"boolean"},"running":{"type":"boolean","description":"Switched on, the plan is valid and the domain is not paused."},"interval_hours":{"type":"number","nullable":true},"paused":{"type":"boolean"},"paused_reason":{"type":"string","nullable":true,"enum":["unreachable","never_reachable","dns","tls",null]},"paused_at":{"type":"string","nullable":true,"format":"date-time"}}},"uptime":{"type":"object","properties":{"enabled":{"type":"boolean"},"interval_minutes":{"type":"number","nullable":true},"is_down":{"type":"boolean","nullable":true},"down_since":{"type":"string","nullable":true,"format":"date-time"},"last_checked_at":{"type":"string","nullable":true,"format":"date-time"},"last_error":{"type":"string","nullable":true}}}}},"FindingCounts":{"type":"object","properties":{"critical":{"type":"integer"},"high":{"type":"integer"},"medium":{"type":"integer"},"low":{"type":"integer"},"info":{"type":"integer"}}},"ReportSummary":{"type":"object","properties":{"id":{"type":"string"},"domain":{"type":"string"},"domain_id":{"type":"string","nullable":true},"organization_id":{"type":"string","nullable":true},"status":{"type":"string","enum":["pending","running","completed","failed"]},"overall_score":{"type":"integer","nullable":true,"minimum":0,"maximum":100},"tests_run":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","nullable":true,"format":"date-time"},"completed_at":{"type":"string","nullable":true,"format":"date-time"},"finding_counts":{"$ref":"#/components/schemas/FindingCounts"},"error":{"type":"string","nullable":true}}},"Finding":{"type":"object","properties":{"severity":{"type":"string","enum":["critical","high","medium","low","info"]},"category":{"type":"string","nullable":true},"key":{"type":"string","nullable":true,"description":"Stable identifier of the kind of finding."},"title":{"type":"string"},"description":{"type":"string"},"recommendation":{"type":"string"},"fix_steps":{"type":"array","items":{"type":"string"}}}},"Report":{"allOf":[{"$ref":"#/components/schemas/ReportSummary"},{"type":"object","properties":{"checks":{"type":"object","description":"One entry per test that ran.","additionalProperties":{"type":"object","properties":{"score":{"type":"integer","nullable":true,"minimum":0,"maximum":100},"grade":{"type":"string","nullable":true},"timed_out":{"type":"boolean"},"pending":{"type":"boolean"}}}},"findings":{"type":"array","items":{"$ref":"#/components/schemas/Finding"}}}}]},"Uptime":{"type":"object","properties":{"domain_id":{"type":"string"},"domain":{"type":"string"},"current":{"type":"object","properties":{"is_down":{"type":"boolean","nullable":true},"down_since":{"type":"string","nullable":true,"format":"date-time"},"last_checked_at":{"type":"string","nullable":true,"format":"date-time"}}},"window":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"hours":{"type":"integer"}}},"availability_percent":{"type":"number","nullable":true,"description":"Null when there is no known state in the window. An outage is never rounded away: with any downtime the value is below 100."},"up_seconds":{"type":"integer"},"down_seconds":{"type":"integer"},"no_data_seconds":{"type":"integer"},"incidents":{"type":"array","items":{"type":"object","properties":{"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":"string","nullable":true,"format":"date-time"},"duration_seconds":{"type":"integer"},"error":{"type":"string","nullable":true},"status_code":{"type":"integer","nullable":true}}}},"checks":{"type":"array","description":"Newest first, at most 500.","items":{"type":"object","properties":{"at":{"type":"string","nullable":true,"format":"date-time"},"is_down":{"type":"boolean"},"status_code":{"type":"integer","nullable":true},"response_time_ms":{"type":"integer","nullable":true},"error":{"type":"string","nullable":true}}}}}},"Alert":{"type":"object","properties":{"id":{"type":"string"},"organization_id":{"type":"string","nullable":true},"domain_id":{"type":"string","nullable":true},"domain":{"type":"string"},"type":{"type":"string","enum":["ssl_change","dns_change","header_change","exposure_found","score_drop","new_finding","uptime_down","monitoring_paused","monitoring_resumed"]},"severity":{"type":"string","enum":["low","medium","high","critical"]},"message":{"type":"string"},"acknowledged":{"type":"boolean"},"created_at":{"type":"string","nullable":true,"format":"date-time"}}}}}}