Partner Portal API v1

The Partner Portal API provides programmatic access to your Holm Security portal data. Currently supports product usage, company management, and reporting periods for MSSP partners. All responses are JSON. Dates use YYYY-MM-DD format in the Europe/Stockholm timezone.

Base URL

https://portal-api.holmsecurity.com/v1

Content Type

All requests and responses use application/json; charset=utf-8.

Products

The API uses short product codes to identify Holm Security products:

CodeProduct
SNSSystem & Network Security
WASWeb Application Security
PATPhishing & Awareness Training
CSCloud Security
DADevice Agent (System & Network Security — Computers)

Quick Start

Get up and running in minutes. Follow these steps to make your first API call.

1

Get your key pair

Log in to the Partner Portal, go to Settings. You will find two keys:

  • Organizer Key (format hsp_org_...) — always visible in the portal
  • API Key (format hsp_...) — shown only once when generated. Click Generate API Key and copy it immediately.
2

Create a session

Use your key pair to create a session. The returned session token is used for all subsequent API calls:

curl -s -X POST https://portal-api.holmsecurity.com/v1/auth/session \
  -H "Content-Type: application/json" \
  -d '{"organizer_key": "hsp_org_your_organizer_key", "api_key": "hsp_your_api_key"}' | jq .
import requests

BASE_URL = "https://portal-api.holmsecurity.com/v1"

# Create a session with your key pair
session_resp = requests.post(f"{BASE_URL}/auth/session", json={
    "organizer_key": "hsp_org_your_organizer_key",
    "api_key": "hsp_your_api_key"
})
session = session_resp.json()
SESSION_TOKEN = session["session_token"]
print(f"Session created, expires at: {session['expires_at']}")

# Use session token for all subsequent requests
headers = {"Authorization": f"Session {SESSION_TOKEN}"}
const BASE_URL = "https://portal-api.holmsecurity.com/v1";

// Create a session with your key pair
const sessionRes = await fetch(`${BASE_URL}/auth/session`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    organizer_key: "hsp_org_your_organizer_key",
    api_key: "hsp_your_api_key"
  })
});
const session = await sessionRes.json();
const SESSION_TOKEN = session.session_token;
console.log(`Session created, expires at: ${session.expires_at}`);

// Use session token for all subsequent requests
const headers = { "Authorization": `Session ${SESSION_TOKEN}` };
3

List your companies

Retrieve the companies you manage. Each company is identified by its security_center_id which follows the format SE-ARNXXXX where XXXX is a 4-digit number.

curl -s https://portal-api.holmsecurity.com/v1/companies \
  -H "Authorization: Session pps_your_session_token" | jq .
resp = requests.get(f"{BASE_URL}/companies", headers=headers)
companies = resp.json()["results"]
for c in companies:
    print(f"{c['security_center_id']}  {c['company_name']}")
const res = await fetch(`${BASE_URL}/companies`, { headers });
const { results } = await res.json();
results.forEach(c => console.log(c.security_center_id, c.company_name));
4

Fetch usage data

Get the latest product usage across all your companies, or for a specific reporting period:

# Latest usage for all companies, product SNS
curl -s "https://portal-api.holmsecurity.com/v1/usage/product?mode=latest&product=SNS" \
  -H "Authorization: Session pps_your_session_token" | jq .

# Peak usage for reporting period January 2026
curl -s "https://portal-api.holmsecurity.com/v1/usage/product?mode=period&reporting_year=2026&reporting_period=01&view=peaks" \
  -H "Authorization: Session pps_your_session_token" | jq .

# Daily usage for a specific company
curl -s "https://portal-api.holmsecurity.com/v1/usage/product?mode=period&reporting_year=2026&reporting_period=01&view=daily&security_center_id=SE-ARNXXXX" \
  -H "Authorization: Session pps_your_session_token" | jq .
import requests, time

# headers already set from step 2: {"Authorization": f"Session {SESSION_TOKEN}"}

# Latest usage for all companies
resp = requests.get(f"{BASE_URL}/usage/product", headers=headers,
                    params={"mode": "latest", "product": "SNS"})
data = resp.json()
print(f"Data as of: {data['as_of']}")
for row in data["results"]:
    print(f"  {row['security_center_id']}: {row['usage_value']}")

# Respect rate limit (1 req/sec)
time.sleep(1)

# Peak usage for a reporting period
resp = requests.get(f"{BASE_URL}/usage/product", headers=headers,
                    params={"mode": "period", "reporting_year": 2026,
                            "reporting_period": "01", "view": "peaks"})
peaks = resp.json()
for total in peaks.get("totals", []):
    print(f"  {total['product']}: peak sum = {total['total_peak_sum']}")
// headers already set from step 2: { "Authorization": `Session ${SESSION_TOKEN}` }

// Helper with rate-limit handling
async function apiGet(path, params = {}) {
  const url = new URL(`${BASE_URL}${path}`);
  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));

  const res = await fetch(url, { headers });
  if (res.status === 429) {
    const retryMs = (await res.json()).retry_after_ms || 1000;
    await new Promise(r => setTimeout(r, retryMs));
    return apiGet(path, params);  // retry once
  }
  return res.json();
}

// Latest usage
const latest = await apiGet("/usage/product", { mode: "latest", product: "SNS" });
console.log(`Data as of: ${latest.as_of}`);
latest.results.forEach(r =>
  console.log(`  ${r.security_center_id}: ${r.usage_value}`)
);

// Peak usage for a period
const peaks = await apiGet("/usage/product", {
  mode: "period", reporting_year: "2026",
  reporting_period: "01", view: "peaks"
});
peaks.totals.forEach(t =>
  console.log(`  ${t.product}: peak sum = ${t.total_peak_sum}`)
);
Tip: The API is rate-limited to 1 request per second. Add a 1-second delay between calls, or handle 429 responses with the retry_after_ms value.

Common Workflows

Generate a monthly billing report

Fetch peak usage for the previous reporting period to calculate billing totals:

# 1. Check which periods are available
curl -s https://portal-api.holmsecurity.com/v1/reporting-periods/available \
  -H "Authorization: Session $SESSION_TOKEN" | jq .

# 2. Get peaks for the previous period (e.g. 2026-01)
curl -s "https://portal-api.holmsecurity.com/v1/usage/product?mode=period&reporting_year=2026&reporting_period=01&view=peaks" \
  -H "Authorization: Session $SESSION_TOKEN" | jq .

# The response includes "totals" with total_peak_sum per product
import requests, time

BASE_URL = "https://portal-api.holmsecurity.com/v1"
SESSION_TOKEN = "pps_your_session_token"  # from POST /v1/auth/session
headers = {"Authorization": f"Session {SESSION_TOKEN}"}

# 1. Find available periods
periods = requests.get(f"{BASE_URL}/reporting-periods/available",
                       headers=headers).json()["results"]
# Pick the most recent completed (non-partial) period
billing_period = next(p for p in periods if not p["is_partial"])
print(f"Billing period: {billing_period['year']}-{billing_period['period']}")
print(f"  Range: {billing_period['from']} to {billing_period['to']}")

time.sleep(1)  # respect rate limit

# 2. Fetch peak usage with totals
resp = requests.get(f"{BASE_URL}/usage/product", headers=headers,
    params={
        "mode": "period",
        "reporting_year": billing_period["year"],
        "reporting_period": billing_period["period"],
        "view": "peaks"
    })
data = resp.json()

# 3. Print billing summary
print("\nBilling Summary:")
for total in data["totals"]:
    print(f"  {total['product']} ({total['metric']}): "
          f"peak sum = {total['total_peak_sum']} "
          f"across {total['company_count']} companies")

# 4. Per-company breakdown
print("\nPer-company peaks:")
for peak in data["peaks"]:
    val = peak["peak_value"] if peak["peak_value"] is not None else "N/A"
    print(f"  {peak['company_name']} ({peak['security_center_id']}): "
          f"{peak['product']} = {val}")

Paginate through all companies

Use limit and offset to iterate through large result sets:

import requests, time

def get_all_companies(base_url, headers):
    """Fetch all companies, handling pagination and rate limits."""
    companies = []
    offset = 0
    limit = 100

    while True:
        resp = requests.get(f"{base_url}/companies", headers=headers,
                            params={"limit": limit, "offset": offset})

        if resp.status_code == 429:
            retry_ms = resp.json().get("retry_after_ms", 1000)
            time.sleep(retry_ms / 1000)
            continue

        data = resp.json()
        companies.extend(data["results"])

        if data["next"] is None:
            break

        offset += limit
        time.sleep(1)  # respect rate limit

    return companies

companies = get_all_companies(BASE_URL, headers)
print(f"Total companies: {len(companies)}")
async function getAllCompanies(baseUrl, headers) {
  const companies = [];
  let offset = 0;
  const limit = 100;

  while (true) {
    const url = `${baseUrl}/companies?limit=${limit}&offset=${offset}`;
    const res = await fetch(url, { headers });

    if (res.status === 429) {
      const { retry_after_ms } = await res.json();
      await new Promise(r => setTimeout(r, retry_after_ms || 1000));
      continue;
    }

    const data = await res.json();
    companies.push(...data.results);

    if (!data.next) break;
    offset += limit;
    await new Promise(r => setTimeout(r, 1000)); // rate limit
  }

  return companies;
}

const companies = await getAllCompanies(BASE_URL, headers);
console.log(`Total companies: ${companies.length}`);

Export daily usage to CSV

Pull daily usage for a period and write it to a CSV file:

import requests, csv, time

BASE_URL = "https://portal-api.holmsecurity.com/v1"
SESSION_TOKEN = "pps_your_session_token"  # from POST /v1/auth/session
headers = {"Authorization": f"Session {SESSION_TOKEN}"}

# Fetch daily usage for January 2026
all_rows = []
offset = 0

while True:
    resp = requests.get(f"{BASE_URL}/usage/product", headers=headers,
        params={
            "mode": "period",
            "reporting_year": 2026,
            "reporting_period": "01",
            "view": "daily",
            "limit": 1000,
            "offset": offset
        })

    if resp.status_code == 429:
        time.sleep(resp.json().get("retry_after_ms", 1000) / 1000)
        continue

    data = resp.json()
    all_rows.extend(data["results"])

    if data["next"] is None:
        break
    offset += 1000
    time.sleep(1)

# Write to CSV
with open("usage_2026_01.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=[
        "date", "security_center_id", "company_name",
        "product", "metric", "usage_value"
    ])
    writer.writeheader()
    writer.writerows(all_rows)

print(f"Exported {len(all_rows)} rows to usage_2026_01.csv")

Authentication

The API uses a key pair + session authentication system. Partners authenticate with two keys to create a short-lived session, then use the session token for all API calls.

Key Pair

Each partner organization receives two keys from the Partner Portal settings:

KeyFormatDescription
Organizer Keyhsp_org_...Always visible in portal settings. Identifies your organization.
API Keyhsp_...Shown only once when generated. Used together with the organizer key to create sessions.

Authentication Flow

  1. Send a POST /v1/auth/session request with your organizer_key and api_key in the request body.
  2. Receive a session token (format pps_...) in the response.
  3. Include the session token in all subsequent API requests using the Authorization header:
Authorization: Session <session-token>

Session Properties

Origin Restrictions

API keys can optionally have allowed origins (IP addresses or domains) configured in portal settings. When configured, session creation requests are only accepted from those origins. Sessions are always locked to the IP that created them, regardless of whether allowed origins are configured.

Scopes

Sessions inherit the scopes assigned to the API key. A session may have multiple scopes.

ScopeDescription
me:readRead identity endpoint
companies:readList and read companies accessible to the session
products:readList products, product coverage, and availability
usage:readRead usage endpoints (latest, period, peaks, daily)
reporting-periods:readRead reporting period helper endpoints

Error Responses

401 Unauthorized — Missing, invalid, or expired session token

{
  "description": "Authentication credentials were not provided."
}

403 Forbidden — Valid session but missing required scope, IP mismatch, or company not accessible

{
  "description": "Permission denied",
  "errors": {
    "scope": ["Missing required scope: usage:read"]
  }
}

409 Conflict — Maximum sessions exceeded (when creating a new session)

{
  "description": "Session limit exceeded",
  "errors": {
    "session": ["Maximum of 5 active sessions per organization. Invalidate an existing session before creating a new one."]
  }
}
POST /v1/auth/session Create a new session

Authentication

No session token required. Uses organizer key and API key in the request body.

Request Body

FieldTypeRequiredDescription
organizer_keystringYesYour organizer key (format hsp_org_...)
api_keystringYesYour API key (format hsp_...)

Example Request

curl -X POST https://portal-api.holmsecurity.com/v1/auth/session \
  -H "Content-Type: application/json" \
  -d '{
    "organizer_key": "hsp_org_your_organizer_key",
    "api_key": "hsp_your_api_key"
  }'

Response 201 Created

{
  "session_token": "pps_abc123def456...",
  "expires_at": "2026-03-11T15:30:00Z",
  "valid_for_seconds": 3600,
  "scopes": ["me:read", "companies:read", "products:read", "usage:read", "reporting-periods:read"],
  "locked_to_origin": "203.0.113.42",
  "message": "Session created successfully"
}

Error Responses

401 Unauthorized — Invalid organizer key or API key

403 Forbidden — Request origin not in allowed origins list

409 Conflict — Maximum 5 active sessions per organization reached

POST /v1/auth/session/validate Check if a session is valid

Authentication

Requires Authorization: Session <token> header.

Example Request

curl -X POST https://portal-api.holmsecurity.com/v1/auth/session/validate \
  -H "Authorization: Session pps_your_session_token"

Response 200 OK

{
  "valid": true,
  "expires_at": "2026-03-11T15:30:00Z",
  "scopes": ["me:read", "companies:read", "products:read", "usage:read", "reporting-periods:read"],
  "locked_to_origin": "203.0.113.42"
}

Error Responses

401 Unauthorized — Invalid or expired session token

DELETE /v1/auth/session Invalidate a session

Authentication

Requires Authorization: Session <token> header.

Example Request

curl -X DELETE https://portal-api.holmsecurity.com/v1/auth/session \
  -H "Authorization: Session pps_your_session_token"

Response 200 OK

{
  "message": "Session invalidated successfully"
}

Error Responses

401 Unauthorized — Invalid or already expired session token

Rate Limiting

The API enforces a limit of 1 request per second per session.

If exceeded, the API returns HTTP 429 Too Many Requests:

{
  "description": "Rate limit exceeded",
  "retry_after_ms": 750
}

Response headers include:

HeaderDescription
Retry-AfterSeconds to wait (integer)
X-Retry-After-MsMilliseconds to wait
X-RateLimit-LimitAlways 1
X-RateLimit-Remaining0 when limited
Wait retry_after_ms then retry. Use exponential backoff if repeated 429s occur.

Error Format

All error responses follow a consistent structure:

{
  "description": "Request failed",
  "errors": {
    "field": ["message"]
  }
}
GET /v1/me Partner identity and scopes

Required Scope

me:read

Example Request

curl https://portal-api.holmsecurity.com/v1/me \
  -H "Authorization: Session $SESSION_TOKEN"

Response

{
  "partner_id": "p_9001",
  "partner_name": "NordSec Solutions AB",
  "scopes": ["me:read", "companies:read", "products:read", "usage:read", "reporting-periods:read"],
  "timezone": "Europe/Stockholm"
}
GET /v1/companies List accessible companies

Required Scope

companies:read

Example Requests

# List all companies
curl https://portal-api.holmsecurity.com/v1/companies \
  -H "Authorization: Session $SESSION_TOKEN"

# Search by name
curl "https://portal-api.holmsecurity.com/v1/companies?search=acme" \
  -H "Authorization: Session $SESSION_TOKEN"

# Paginate (page 2, 50 per page)
curl "https://portal-api.holmsecurity.com/v1/companies?limit=50&offset=50" \
  -H "Authorization: Session $SESSION_TOKEN"

Query Parameters

NameTypeDescription
searchstringFilter by name or ID
statusstringFilter by status
limitintegerResults per page (default 100, max 1000)
offsetintegerPagination offset (default 0)

Response

{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "Teknikbolaget AB",
      "status": "active"
    },
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "Nordic Cloud Oy",
      "status": "active"
    },
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "DataVakt AS",
      "status": "active"
    }
  ]
}
GET /v1/companies/{security_center_id} Get a single company

Required Scope

companies:read

Example Request

curl https://portal-api.holmsecurity.com/v1/companies/SE-ARNXXXX \
  -H "Authorization: Session $SESSION_TOKEN"

Path Parameters

NameTypeDescription
security_center_idstringThe company's Security Center ID

Response

{
  "security_center_id": "SE-ARNXXXX",
  "company_name": "Teknikbolaget AB",
  "status": "active",
  "created_at": "2024-03-01"
}
GET /v1/products List products across all companies

Required Scope

products:read

Example Request

curl https://portal-api.holmsecurity.com/v1/products \
  -H "Authorization: Session $SESSION_TOKEN"

Response

{
  "results": [
    { "product": "SNS", "company_count": 120 },
    { "product": "WAS", "company_count": 98 },
    { "product": "PAT", "company_count": 85 },
    { "product": "CS", "company_count": 42 },
    { "product": "DA", "company_count": 67 }
  ]
}
GET /v1/companies/{security_center_id}/products Products for a specific company

Required Scopes

products:read companies:read

Example Request

curl https://portal-api.holmsecurity.com/v1/companies/SE-ARNXXXX/products \
  -H "Authorization: Session $SESSION_TOKEN"

Response

{
  "security_center_id": "SE-ARNXXXX",
  "company_name": "Teknikbolaget AB",
  "results": [
    { "product": "SNS" },
    { "product": "WAS" },
    { "product": "PAT" }
  ]
}
GET /v1/coverage/products Product coverage across companies

Required Scope

products:read

Example Requests

# Summary (all products)
curl https://portal-api.holmsecurity.com/v1/coverage/products \
  -H "Authorization: Session $SESSION_TOKEN"

# Detailed (specific product: which companies have it)
curl "https://portal-api.holmsecurity.com/v1/coverage/products?product=SNS" \
  -H "Authorization: Session $SESSION_TOKEN"

Query Parameters

NameTypeDescription
productstringFilter by product code. When provided, returns detailed enabled/disabled lists.

Summary Response

{
  "results": [
    {
      "product": "SNS",
      "enabled_company_count": 47,
      "total_company_count": 52
    }
  ]
}

Detailed Response (with product filter)

{
  "product": "SNS",
  "enabled_security_center_ids": ["SE-ARNXXXX", "SE-ARNXXXX", "SE-ARNXXXX"],
  "disabled_security_center_ids": ["SE-ARNXXXX", "SE-ARNXXXX"]
}
GET /v1/reporting-periods/current Current reporting period

Required Scope

reporting-periods:read

Example Request

curl https://portal-api.holmsecurity.com/v1/reporting-periods/current \
  -H "Authorization: Session $SESSION_TOKEN"

Reporting Period Definition

Period MM covers the 26th of the previous month through the 25th of month MM (inclusive). For the current period, to is capped at the latest available processed date.

Response

{
  "timezone": "Europe/Stockholm",
  "current_period": {
    "year": 2026,
    "period": "02",
    "from": "2026-01-26",
    "to": "2026-02-23",
    "is_partial": true
  }
}
GET /v1/reporting-periods/available Current + two previous periods

Required Scope

reporting-periods:read

Example Request

curl https://portal-api.holmsecurity.com/v1/reporting-periods/available \
  -H "Authorization: Session $SESSION_TOKEN"

Response

{
  "results": [
    {
      "year": 2026,
      "period": "02",
      "from": "2026-01-26",
      "to": "2026-02-23",
      "is_partial": true
    },
    {
      "year": 2026,
      "period": "01",
      "from": "2025-12-26",
      "to": "2026-01-25",
      "is_partial": false
    },
    {
      "year": 2025,
      "period": "12",
      "from": "2025-11-26",
      "to": "2025-12-25",
      "is_partial": false
    }
  ]
}
Only the current period and two previous periods are available. Requesting older periods returns 400.
GET /v1/usage/product Product usage (latest, daily, peaks)

Required Scope

usage:read

Example Requests

# Latest usage for product SNS
curl "https://portal-api.holmsecurity.com/v1/usage/product?mode=latest&product=SNS" \
  -H "Authorization: Session $SESSION_TOKEN"

# Period peaks for January 2026
curl "https://portal-api.holmsecurity.com/v1/usage/product?mode=period&reporting_year=2026&reporting_period=01&view=peaks" \
  -H "Authorization: Session $SESSION_TOKEN"

# Daily usage for a specific company
curl "https://portal-api.holmsecurity.com/v1/usage/product?mode=period&reporting_year=2026&reporting_period=01&view=daily&security_center_id=SE-ARNXXXX" \
  -H "Authorization: Session $SESSION_TOKEN"

# Everything (peaks + daily) for all companies, all products
curl "https://portal-api.holmsecurity.com/v1/usage/product?mode=period&reporting_year=2026&reporting_period=01&view=all" \
  -H "Authorization: Session $SESSION_TOKEN"

Query Parameters

NameTypeRequiredDescription
modestringYeslatest or period
reporting_yearintegerIf periodYear (e.g. 2026)
reporting_periodstringIf periodMonth (e.g. 01)
viewstringNolatest (default for latest mode), all | peaks | daily (for period mode)
security_center_idstringNoFilter to a single company (omit = all)
productstringNoFilter by product code (e.g. SNS)
metricstringNoDefault: ips
limitintegerNoPagination limit (default 100, max 1000)
offsetintegerNoPagination offset (default 0)

Mode: latest

Returns the most recent usage values for each company.

{
  "mode": "latest",
  "view": "latest",
  "timezone": "Europe/Stockholm",
  "as_of": "2026-02-23",
  "filters": {
    "security_center_id": null,
    "product": "SNS",
    "metric": "ips"
  },
  "count": 3,
  "next": null,
  "previous": null,
  "results": [
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "Teknikbolaget AB",
      "product": "SNS",
      "metric": "ips",
      "date": "2026-02-23",
      "usage_value": 254
    },
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "Nordic Cloud Oy",
      "product": "SNS",
      "metric": "ips",
      "date": "2026-02-23",
      "usage_value": 128
    },
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "Berglund Fastigheter AB",
      "product": "SNS",
      "metric": "ips",
      "date": "2026-02-23",
      "usage_value": null,
      "null_reason": "product_not_enabled_for_company"
    }
  ]
}

Mode: period — view=peaks

Returns peak usage per company for a reporting period, plus totals.

{
  "mode": "period",
  "view": "peaks",
  "timezone": "Europe/Stockholm",
  "reporting_period": {
    "year": 2026,
    "period": "01",
    "from": "2025-12-26",
    "to": "2026-01-25",
    "is_partial": false
  },
  "totals": [
    {
      "product": "SNS",
      "metric": "ips",
      "total_peak_sum": 648,
      "company_count": 3,
      "null_company_count": 0
    }
  ],
  "peaks": [
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "Teknikbolaget AB",
      "product": "SNS",
      "metric": "ips",
      "peak_value": 312,
      "peak_date": "2026-01-14"
    },
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "Nordic Cloud Oy",
      "product": "SNS",
      "metric": "ips",
      "peak_value": 198,
      "peak_date": "2026-01-08"
    },
    {
      "security_center_id": "SE-ARNXXXX",
      "company_name": "DataVakt AS",
      "product": "SNS",
      "metric": "ips",
      "peak_value": 138,
      "peak_date": "2026-01-20"
    }
  ]
}

Mode: period — view=daily

Returns daily usage rows only (paginated). No peaks or totals.

Mode: period — view=all (default)

Returns both peaks and daily rows in a single response.

Null handling: When a product filter is set and a company does not have that product, the company is still represented with usage_value: null and null_reason: "product_not_enabled_for_company".

Scope Requirements Summary

EndpointMethodRequired Scopes
/v1/auth/sessionPOSTNone (uses key pair)
/v1/auth/session/validatePOSTValid session
/v1/auth/sessionDELETEValid session
/v1/meGETme:read
/v1/companiesGETcompanies:read
/v1/companies/{id}GETcompanies:read
/v1/productsGETproducts:read
/v1/companies/{id}/productsGETproducts:read, companies:read
/v1/coverage/productsGETproducts:read
/v1/reporting-periods/currentGETreporting-periods:read
/v1/reporting-periods/availableGETreporting-periods:read
/v1/usage/productGETusage:read