# API Docs

## API Details

* [Authentication](#authentication)
* [Endpoint](#endpoint)
* [Requests](#requests)
  * [Pagination](#pagination)
  * [Introspection](#introspection)
  * [Search](#search)
  * [Ordering](#ordering)
* [Use Cases](#use-cases)

### Authentication

The primary method of authenticating your API is via Security Tokens. You can obtain your security token by logging into the dashboard and following the steps below.

* Login to the [Interlynk's dashboard](https://app.interlynk.io)
* Click on settings on the left-hand bar ![](https://1251112834-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FJtNO83UTnOy9Rz0sEUiW%2Fuploads%2F569Di5tkWduMEIdhaz8a%2Fimage.png?alt=media\&token=4c303dab-dd51-4396-8315-8b278b7efe55)
* Click on "Personal" on the top right.
* Click on "Security Tokens"
* Click on "+" to generate a new token.

You can name your security token at this stage and set its expiration date. Depending on the type of integration being done, its recommended to choose the appropriate expiration date. Interlynk's security token looks like `lynk_live_CgzGW2qLk5C73o7SgsKyBT3wVcm*********`

Please remember to copy the token once generated, you will **not** be able to retrieve it once you close the window.&#x20;

The security token assumes the role of the user who created it. If a user with admin role creates the token it will have admin privileges.&#x20;

### Endpoint

Interlynk's GraphQL has a single endpoint.  The same endpoint supports both queries and mutations.&#x20;

```http
https://api.interlynk.io/lynkapi
```

### Requests

The curl command below demonstrates a simple call to the endpoint to retrieve your organizations name.&#x20;

{% tabs %}
{% tab title="Curl" %}

<pre class="language-sh"><code class="lang-sh">curl 'https://api.interlynk.io/lynkapi' \
  -H 'authorization: Bearer <a data-footnote-ref href="#user-content-fn-1">lynk_live</a>_&#x3C;SECURITY_TOKEN>' \
  -H 'content-type: application/json' \
  -d '{
    "operationName": "GetOrgName",
    "variables": {},
    "query": "query GetOrgName { organization { name } }"
  }'
</code></pre>

{% endtab %}

{% tab title="PowerShell" %}

```powershell
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession

Invoke-WebRequest -UseBasicParsing -Uri "https://api.interlynk.io/lynkapi" -Method "POST" -WebSession $session -Headers @{
    "authorization" = "Bearer lynk_live_<SECURITY_TOKEN>"
} -ContentType "application/json" -Body '{
    "operationName": "GetOrgName",
    "variables": {},
    "query": "query GetOrgName { organization { name } }"
}'
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'net/http'
require 'uri'
require 'json'

uri = URI.parse("https://api.interlynk.io/lynkapi")
r
header = {
  "Authorization" => "Bearer lynk_live_<SECURITY_TOKEN>",
  "Content-Type" => "application/json"
}

body = {
  operationName: "GetOrgName",
  variables: {},
  query: "query GetOrgName { organization { name } }"
}.to_json

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri.request_uri, header)
request.body = body

response = http.request(request)

puts response.body

```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var url = "https://api.interlynk.io/lynkapi";
        var securityToken = "lynk_live_<SECURITY_TOKEN>"; // Replace with your actual token

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("Authorization", $"Bearer {securityToken}");
            client.DefaultRequestHeaders.Add("Content-Type", "application/json");

            var jsonBody = new
            {
                operationName = "GetOrgName",
                variables = new { },
                query = "query GetOrgName { organization { name } }"
            };

            var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(jsonBody), Encoding.UTF8, "application/json");

            var response = await client.PostAsync(url, content);

            string responseString = await response.Content.ReadAsStringAsync();
            Console.WriteLine(responseString);
        }
    }
}

```

{% endtab %}
{% endtabs %}

#### Pagination

For most of the queries we support cursor based pagination.  Cursor-based pagination is an efficient method for traversing large datasets in GraphQL. It uses a unique identifier (cursor) to determine the starting point for each subsequent query, allowing for consistent and performant data retrieval.

For all queries we always provide the total number of records for each query. If the number of entries to be returned is not provided it always defaults to **25**.&#x20;

The below example provides an example of a pagination request.&#x20;

{% tabs %}
{% tab title="Request" %}

```graphql
query GetProjectGroups() {
  organization {
    id
    projectGroups(
      first: $first
      last: $last
      after: $after
      before: $before
    ) {
      totalCount
      pageInfo {
        endCursor
        hasNextPage
        startCursor
        hasPreviousPage
        __typename
      }
      nodes {
        id
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "organization": {
      "id": "72219448-e3cf-47f4-8e54-49199fc47f52",
      "projectGroups": {
        "totalCount": 3,
        "pageInfo": {
          "endCursor": "Mw",
          "hasNextPage": false,
          "startCursor": "MQ",
          "hasPreviousPage": false
        },
        "nodes": [
          {
            "id": "006b70d7-0cb9-4194-ad81-7fb03a886b09",
          }
        ]      
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

Things to note&#x20;

* ProjectGroup takes params to control the pagination e.g first, last, after, before
  * (first, after): these params  go together, first 100 starting after this offset.&#x20;
  * (last, before): these params go together, last 30 before this offset.&#x20;
* totalCount is requested, which provides the total number of items in the dataset.
* PageInfo: Server provided response which can be used for the next request.&#x20;
  * (hasNextPage, endCursor): these allow for forward progression, if hasNextPage is true.
  * (hasPreviousPage, startCursor): these allow for backward progression, if hasPreviousPage is true.

#### Introspection&#x20;

The production endpoint does **not** support introspection.

#### Search

For most of the queries we support search. The search fields are context dependent. &#x20;

#### Ordering

For most of the queries we support ordering.&#x20;

### Use-cases

Below are a list of use cases which can help you build integrations&#x20;

#### Get all projects and associated environments

{% tabs %}
{% tab title="Request" %}

```graphql
query GetProjectsAndEnv($search: String, $enabled: Boolean, $first: Int, $last: Int, $after: String, $before: String, $field: ProjectGroupOrderByFields!, $direction: OrderByDirection!) {
  organization {
    id
    projectGroups(
      search: $search
      enabled: $enabled
      first: $first
      last: $last
      after: $after
      before: $before
      orderBy: {field: $field, direction: $direction}
    ) {
      totalCount
      pageInfo {
        endCursor
        hasNextPage
        startCursor
        hasPreviousPage
      }
      nodes {
        id
        name
        enabled
        versionCount: sbomsCount
        updatedAt
        environments: projects {
          id
          name
          versionCount: sbomsCount
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Variables" %}

```
{
  "field": "PROJECT_GROUPS_UPDATED_AT",
  "direction": "DESC",
  "enabled": true,
  "first": 25
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "organization": {
      "id": "72219448-e3cf-47f4-8e54-49199fc47f52",
      "projectGroups": {
        "totalCount": 3,
        "pageInfo": {
          "endCursor": "Mw",
          "hasNextPage": false,
          "startCursor": "MQ",
          "hasPreviousPage": false
        },
        "nodes": [
          {
            "id": "006b70d7-0cb9-4194-ad81-7fb03a886b09",
            "name": "aqqq",
            "enabled": true,
            "versionCount": 1,
            "updatedAt": "2024-08-04T01:00:29Z",
            "environments": [
              {
                "id": "d70a3087-a8cd-4795-883b-d1c505b6244d",
                "name": "default",
                "versionCount": 1
              },
              {
                "id": "c549a170-573b-49d5-adf6-b00777a6bab1",
                "name": "development",
                "versionCount": 0
              },
              {
                "id": "37e7cc15-b4d3-4af6-b04c-656d81df81e0",
                "name": "production",
                "versionCount": 0
              },
              ....
              
            ]
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

#### List versions for project id

{% tabs %}
{% tab title="Request" %}

```graphql
query GetVersions($id: Uuid!, $first: Int, $after: String, $last: Int, $before: String, $search: String, $field: SbomOrderByFields!, $direction: OrderByDirection!) {
  project(id: $id) {
    id
    versions: sbomVersions(
      first: $first
      after: $after
      last: $last
      before: $before
      search: $search
      orderBy: {direction: $direction, field: $field}
    ) {
      totalCount
      pageInfo {
        endCursor
        hasNextPage
        startCursor
        hasPreviousPage
      }
      nodes {
        id
        createdAt
        updatedAt
        lifecycle
        projectVersion
        vulnRunStatus
      }
    }
  }
}

```

{% endtab %}

{% tab title="Variables" %}

```json
{
  "id": "d70a3087-a8cd-4795-883b-d1c505b6244d",
  "field": "SBOMS_CREATED_AT",
  "direction": "DESC",
  "first": 25
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "project": {
      "id": "d70a3087-a8cd-4795-883b-d1c505b6244d",
      "versions": {
        "totalCount": 1,
        "pageInfo": {
          "endCursor": "MQ",
          "hasNextPage": false,
          "startCursor": "MQ",
          "hasPreviousPage": false
        },
        "nodes": [
          {
            "id": "966a5022-6ff5-4768-909c-0181f9804540",
            "createdAt": "2024-08-01T00:00:04Z",
            "updatedAt": "2024-08-04T01:00:29Z",
            "lifecycle": "edited",
            "projectVersion": "2.0.31",
            "vulnRunStatus": "FINISHED"
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

#### Upload SBOM as version for Project

{% tabs %}
{% tab title="Request" %}

```graphql
mutation UploadSbom($doc: Upload!, $projectId: ID!) {
  sbomUpload(input: {doc: $doc, projectId: $projectId}) {
    errors
  }
}
```

{% endtab %}

{% tab title="Variables" %}

```json
{
  "doc": null,
  "projectId": "d70a3087-a8cd-4795-883b-d1c505b6244d"
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "sbomUpload": {
      "errors": []
    }
  }
}
```

{% endtab %}
{% endtabs %}

#### Download CycloneDX SBOM for a version

{% tabs %}
{% tab title="Request" %}

```graphql
query downloadSbom($environmentId: Uuid!, $versionId: Uuid!, $includeVulns: Boolean) {
  sbom(projectId: $environmentId, sbomId: $sbomId) {
    download(sbomId: $versionId, includeVulns: $includeVulns)
    __typename
  }
}
```

{% endtab %}

{% tab title="Variables" %}

```json
{
  "environmentId": "d70a3087-a8cd-4795-883b-d1c505b6244d",
  "versionId": "966a5022-6ff5-4768-909c-0181f9804540",
  "includeVulns": false
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
   "data":{
      "sbom":{
         "download":"Base 64 encoded string"
      }
   }
}
```

{% endtab %}
{% endtabs %}

#### Version Quality Score

{% tabs %}
{% tab title="Request" %}

```graphql
query GetVersionQualityScores($versionIds: [ID!]!, $reportFormat: ComplianceReportFormat) {
  complianceReports(sbomIds: $versionIds, reportFormat: $reportFormat) {
    nodes {
      reportFormat
      score
      scoreByCategory {
        category
        score
      }
    }
  }
}
```

{% endtab %}

{% tab title="Variables" %}

```json
{
  "versionIds": [
    "966a5022-6ff5-4768-909c-0181f9804540"
  ],
  "reportFormat": "NTIA"
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "complianceReports": {
      "nodes": [
        {
          "reportFormat": "NTIA",
          "score": 66.66666666666667,
          "scoreByCategory": [
            {
              "category": "Timestamp",
              "score": 100
            },
            {
              "category": "Supplier Name",
              "score": 0
            },
            {
              "category": "Unique ID",
              "score": 100
            },
            {
              "category": "Author",
              "score": 0
            },
            {
              "category": "Component Name",
              "score": 100
            },
            {
              "category": "Component Version",
              "score": 100
            },
            {
              "category": "Component Supplier Name",
              "score": 0
            },
            {
              "category": "Component Other unique identifiers",
              "score": 100
            },
            {
              "category": "Component Relationships",
              "score": 100
            }
          ],
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

**Version Status**

After an sbom is uploaded to a version, various post-processing tasks are run. Tasks like vulnerability detection, automation and policies.  All of these tasks have statuses associated with them, which can help determine if its the right time to download the SBOM. &#x20;

STARTED and FINISHED are the only two statuses that are displayed.&#x20;

{% tabs %}
{% tab title="Request" %}

```
query GetVersion($projectId: Uuid!, $versionId: Uuid!) {
  sbom(projectId: $projectId, sbomId: $versionId) {
    id
    vulnRunStatus
    automationRunStatus
    policyRunStatus
  }
}
```

{% endtab %}

{% tab title="Variables" %}

```
{
  "projectId": "4f30f239-1058-4858-82f7-083aa5305e2d",
  "versionId": "f460ff4f-324f-4c9a-bafc-fdd76c9372e6"
}
```

{% endtab %}

{% tab title="Response" %}

```
{
  "data": {
    "sbom": {
      "id": "f460ff4f-324f-4c9a-bafc-fdd76c9372e6",
      "vulnRunStatus": "FINISHED",
      "automationRunStatus": "FINISHED",
      "policyRunStatus": "FINISHED"
    }
  }
}
```

{% endtab %}
{% endtabs %}

[^1]:


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.interlynk.io/api/api-docs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
