List Integrations
curl --request GET \
--url https://api.leen.dev/v1/integrations/ \
--header 'X-API-KEY: <api-key>'import requests
url = "https://api.leen.dev/v1/integrations/"
headers = {"X-API-KEY": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-KEY': '<api-key>'}};
fetch('https://api.leen.dev/v1/integrations/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.leen.dev/v1/integrations/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.leen.dev/v1/integrations/"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-KEY", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.leen.dev/v1/integrations/")
.header("X-API-KEY", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.leen.dev/v1/integrations/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-KEY"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"count": 123,
"items": [
{
"categories": [
"VMS"
],
"data_exports": [
"alerts"
],
"description": "<string>",
"icon": "<string>",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"integration_id": "<string>",
"name": "<string>",
"parameters": [
{}
],
"version": "<string>",
"capabilities": {},
"deprecated": false
}
],
"total": 123
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"ctx": {},
"input": "<unknown>"
}
]
}Integration Details
List Integration Details
Filterable catalog of the vendors Leen can connect to, with each vendor’s version, credential parameters, categories and data exports. A superset of the Connectors endpoint.
GET
/
integrations
/
List Integrations
curl --request GET \
--url https://api.leen.dev/v1/integrations/ \
--header 'X-API-KEY: <api-key>'import requests
url = "https://api.leen.dev/v1/integrations/"
headers = {"X-API-KEY": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-KEY': '<api-key>'}};
fetch('https://api.leen.dev/v1/integrations/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.leen.dev/v1/integrations/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.leen.dev/v1/integrations/"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-KEY", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.leen.dev/v1/integrations/")
.header("X-API-KEY", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.leen.dev/v1/integrations/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-KEY"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"count": 123,
"items": [
{
"categories": [
"VMS"
],
"data_exports": [
"alerts"
],
"description": "<string>",
"icon": "<string>",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"integration_id": "<string>",
"name": "<string>",
"parameters": [
{}
],
"version": "<string>",
"capabilities": {},
"deprecated": false
}
],
"total": 123
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"ctx": {},
"input": "<unknown>"
}
]
}An integration is a vendor that Leen can connect to. This endpoint returns the full catalog of supported integrations with their details; the response is the same for every organization, so you can call it without first creating a connection. Use the
Socket’s
Tenable’s
category query parameter to narrow the list to one or more domains.
Each item’s integration_id (for example TENABLE, SNYK, WIZ_CODE) is the exact value to pass as vendor when creating a connection. data_exports lists the data types that integration produces, and categories maps to the domain sections of this documentation (AppSec, EDR, VMS, and so on). parameters describes the credential form fields for that integration — names and types only, never secret values.
Filter capabilities
Some filters on the data export endpoints are tri-state.reachability and exploitable on List Issues, List Vulnerabilities and List Vulnerability Findings are null on every record for which the vendor gave no signal, and a null matches no filter value. Filtering a vendor that never reports the field therefore returns an empty page that looks exactly like “no record matched”. capabilities tells you, per integration and per data export, which of those filters the vendor actually populates and which values it emits, so a filter UI can be built per connection without guessing.
The block is keyed by data export and covers three today. Only filters whose population varies by vendor are declared; date windows, id lists and sort behave the same everywhere and are not listed. Every integration that exports one of these data exports declares the matching block.
| Data export | Endpoint | Filters declared |
|---|---|---|
vulnerability_findings_v2 | List Vulnerability Findings | reachability, exploitable |
issues | List Issues | reachability, exploitable, severity, state, include_type, exclude_type |
vulnerabilities | List Vulnerabilities | reachability, exploitable, severity, state, category, port, protocol, cve |
The contract, as stated in the
capabilities field description in the reference below:Filter support declared by the integration, keyed by data export (for example vulnerability_findings_v2) and then by filter name. Absence is unknown, not unsupported: an integration without capabilities, or a data export or filter missing inside it, makes no claim; only "supported": false is an explicit negative. "supported": true means at least some records carry a non-null value for the filter, not all of them. values_emitted lists the normalized values the integration can produce and is present on every supported filter with a fixed set of values, enumerations and booleans alike; a free-form filter such as cve carries none. notes explains the scope, such as which record types carry the value.vulnerability_findings_v2 block, which populates both filters:
"capabilities": {
"vulnerability_findings_v2": {
"filters": {
"reachability": {
"supported": true,
"values_emitted": ["REACHABLE", "POTENTIALLY_REACHABLE", "UNREACHABLE", "UNKNOWN"],
"notes": "Verdicts Socket could not determine, including analysis pending, errored or unsupported for the package ecosystem, are reported as UNKNOWN."
},
"exploitable": {
"supported": true,
"values_emitted": [true, false],
"notes": "True when the CVE is in the CISA Known Exploited Vulnerabilities catalog and false when Socket reports it is not; findings without vulnerability data carry no value."
}
}
}
}
vulnerabilities block, abridged to five of its eight filters, shows the other shapes: an unsupported filter, a supported boolean, a supported enumeration and two supported free-form filters. Keys the integration does not declare are serialized as explicit null, so values_emitted and notes are always present on the wire: an unsupported filter carries null for both, and a supported free-form filter such as port or cve carries notes but no values_emitted.
"capabilities": {
"vulnerabilities": {
"filters": {
"reachability": {
"supported": false,
"values_emitted": null,
"notes": null
},
"exploitable": {
"supported": true,
"values_emitted": [true, false],
"notes": "Tenable's own exploit_available flag on the plugin: true when Tenable's exploit sources hold an exploit for the plugin, false when Tenable has assessed the plugin and found none. Populated on every finding observed; a plugin without the flag carries no value."
},
"severity": {
"supported": true,
"values_emitted": ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO", "NONE"],
"notes": "Tenable severity 0 to 4 map to INFO, LOW, MEDIUM, HIGH and CRITICAL; any other value is NONE."
},
"port": {
"supported": true,
"values_emitted": null,
"notes": "Populated on every finding; local checks that are not tied to a network service report port 0."
},
"cve": {
"supported": true,
"values_emitted": null,
"notes": "Populated from the plugin's CVE list; plugins without a CVE carry no value."
}
}
}
}
Rendering a filter UI from the block
- A filter that is absent from the block, or has
"supported": false, should be hidden or disabled for that integration. - A filter with
"supported": trueand avalues_emittedlist should offer exactly that list as its options. Do not offer values the API accepts but the vendor never emits; selecting one returns an empty page. - A supported free-form filter (
port,protocol,cve) has novalues_emitted; render it as a text input and readnotesfor the vendor’s conventions.protocolis matched case-sensitively, and vendors differ: Tenable and Rapid7 InsightVM emitTCP, Qualys emitstcp. notesis tooltip text: scope caveats such as which record types carry the value."supported": truemeans some records carry a value. A vendor can reportreachabilityon dependency findings while its SAST findings staynull, so selecting every value invalues_emittedis not the same as applying no filter.
null is not a filter value. All three endpoints reject ?reachability=null and ?exploitable=null with a 422. Omitting the parameter means “no filter”, not “match null”. There is no way today to ask for the records a vendor did not assess.Reading the block
Absence is unknown, not unsupported: an integration withoutcapabilities, or a data export or filter missing inside it, makes no claim either way. "supported": true is “some, not all”, which is why notes matters. values_emitted is the list to offer and is always present on a supported filter with a fixed set of values, whether an enumeration or a boolean (booleans list [true, false]); a free-form filter such as cve never carries one. values_emitted uses the stored, uppercase vocabulary. List Vulnerabilities renders its enumerated fields in lowercase on records ("severity": "high", "reachability": "reachable") and its severity, state, category and reachability filters accept either case; protocol does not.
The filters themselves are documented on List Issues, List Vulnerabilities and List Vulnerability Findings.Authorizations
Query Parameters
Offset index of items (starting from 0)
Required range:
x >= 0Limit size (page size)
Required range:
x >= 0Filter by category, comma separated