End-to-end example

This ties the pieces together into one runnable program per language. It uses the service-account (client-credentials) flow because that runs unattended, with no browser step, so the whole thing fits in one file. The steps are:

  1. Check connectivity with GET /health (no auth).
  2. Authenticate to get an access token.
  3. Call an authenticated endpoint with Authorization: Bearer.
  4. Handle a 401 by getting a fresh token and retrying once.

The authenticated call targets an illustrative self/me?api-version=1.0 resource, to show the shape. Today the only live endpoint is /health; swap in a real resource as more of the API opens up. See Endpoints you call.

Set CSIQ_CLIENT_ID and CSIQ_CLIENT_SECRET in the environment first; never hard-code the secret.

curl
#!/usr/bin/env bash
: "${CSIQ_CLIENT_ID:?Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide}"
: "${CSIQ_CLIENT_SECRET:?Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide}"
set -euo pipefail
# Needs: jq (for parsing the token response).

AUTH="https://auth.cybersentriq.com/"
API="https://api.cybersentriq.com"
AUD="https://api.cybersentriq.com"

# 2. Authenticate (client-credentials). Secret comes from the environment.
get_token() {
  curl -sS -X POST "${AUTH}oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    --data-urlencode "grant_type=client_credentials" \
    --data-urlencode "client_id=$CSIQ_CLIENT_ID" \
    --data-urlencode "client_secret=$CSIQ_CLIENT_SECRET" \
    --data-urlencode "audience=$AUD" | jq -r .access_token
}

# 1. Connectivity (no auth).
curl -sS "$API/health"; echo

TOKEN="$(get_token)"

# 3. Call an authenticated endpoint (illustrative resource).
code=$(curl -sS -o /tmp/csiq_body.json -w '%{http_code}' \
  "$API/self/me?api-version=1.0" -H "Authorization: Bearer $TOKEN")

# 4. On 401, get a fresh token and retry once.
if [ "$code" = "401" ]; then
  TOKEN="$(get_token)"
  curl -sS "$API/self/me?api-version=1.0" -H "Authorization: Bearer $TOKEN"
else
  cat /tmp/csiq_body.json
fi
TypeScript / Node
const AUTH = "https://auth.cybersentriq.com/";
const API = "https://api.cybersentriq.com";
const AUD = "https://api.cybersentriq.com";

const CLIENT_ID = process.env.CSIQ_CLIENT_ID;
const CLIENT_SECRET = process.env.CSIQ_CLIENT_SECRET;
if (!CLIENT_ID) throw new Error("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide");
if (!CLIENT_SECRET) throw new Error("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide");

async function getToken(): Promise<string> {
  const res = await fetch(`${AUTH}oauth/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      audience: AUD,
    }),
  });
  return (await res.json()).access_token;
}

async function main() {
  // 1. Connectivity (no auth).
  console.log(await (await fetch(`${API}/health`)).json());

  // 2. Authenticate.
  let token = await getToken();

  // 3. Call an authenticated endpoint (illustrative resource).
  const call = () =>
    fetch(`${API}/self/me?api-version=1.0`, {
      headers: { Authorization: `Bearer ${token}` },
    });
  let res = await call();

  // 4. On 401, refresh (re-request) and retry once.
  if (res.status === 401) {
    token = await getToken();
    res = await call();
  }
  console.log(res.status, await res.text());
}

main().catch((e) => { console.error(e); process.exit(1); });
Python
import os
import requests

AUTH = "https://auth.cybersentriq.com/"
API = "https://api.cybersentriq.com"
AUD = "https://api.cybersentriq.com"

CLIENT_ID = os.environ.get("CSIQ_CLIENT_ID")
CLIENT_SECRET = os.environ.get("CSIQ_CLIENT_SECRET")
if not CLIENT_ID:
    raise SystemExit("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide")
if not CLIENT_SECRET:
    raise SystemExit("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide")

def get_token():
    res = requests.post(
        f"{AUTH}oauth/token",
        data={
            "grant_type": "client_credentials",
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET,
            "audience": AUD,
        },
    )
    return res.json()["access_token"]

def call(token):
    return requests.get(
        f"{API}/self/me",
        params={"api-version": "1.0"},
        headers={"Authorization": f"Bearer {token}"},
    )

def main():
    # 1. Connectivity (no auth).
    print(requests.get(f"{API}/health").json())

    # 2. Authenticate.
    token = get_token()

    # 3. Call an authenticated endpoint (illustrative resource).
    res = call(token)

    # 4. On 401, refresh (re-request) and retry once.
    if res.status_code == 401:
        token = get_token()
        res = call(token)
    print(res.status_code, res.text)

if __name__ == "__main__":
    main()
C#
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

class Program
{
    const string Auth = "https://auth.cybersentriq.com/";
    const string Api = "https://api.cybersentriq.com";
    const string Aud = "https://api.cybersentriq.com";
    static readonly HttpClient Http = new();
    static readonly string ClientId = Environment.GetEnvironmentVariable("CSIQ_CLIENT_ID")
        ?? throw new InvalidOperationException("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide");
    static readonly string ClientSecret = Environment.GetEnvironmentVariable("CSIQ_CLIENT_SECRET")
        ?? throw new InvalidOperationException("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide");

    static async Task<string> GetToken()
    {
        var form = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["grant_type"] = "client_credentials",
            ["client_id"] = ClientId,
            ["client_secret"] = ClientSecret,
            ["audience"] = Aud,
        });
        var res = await Http.PostAsync($"{Auth}oauth/token", form);
        using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
        return doc.RootElement.GetProperty("access_token").GetString();
    }

    static async Task<HttpResponseMessage> Call(string token)
    {
        var req = new HttpRequestMessage(HttpMethod.Get, $"{Api}/self/me?api-version=1.0");
        req.Headers.Add("Authorization", $"Bearer {token}");
        return await Http.SendAsync(req);
    }

    static async Task Main()
    {
        // 1. Connectivity (no auth).
        Console.WriteLine(await Http.GetStringAsync($"{Api}/health"));

        // 2. Authenticate.
        var token = await GetToken();

        // 3 + 4. Call, and on 401 refresh (re-request) and retry once.
        var res = await Call(token);
        if (res.StatusCode == HttpStatusCode.Unauthorized)
        {
            token = await GetToken();
            res = await Call(token);
        }
        Console.WriteLine((int)res.StatusCode + " " + await res.Content.ReadAsStringAsync());
    }
}
Go
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "net/url"
    "os"
)

const (
    authBase = "https://auth.cybersentriq.com/"
    apiBase  = "https://api.cybersentriq.com"
    audience = "https://api.cybersentriq.com"
)

func getToken(clientID, clientSecret string) (string, error) {
    form := url.Values{
        "grant_type":    {"client_credentials"},
        "client_id":     {clientID},
        "client_secret": {clientSecret},
        "audience":      {audience},
    }
    res, err := http.PostForm(authBase+"oauth/token", form)
    if err != nil {
        return "", err
    }
    defer res.Body.Close()
    var tok struct {
        AccessToken string `json:"access_token"`
    }
    if err := json.NewDecoder(res.Body).Decode(&tok); err != nil {
        return "", err
    }
    return tok.AccessToken, nil
}

func call(token string) (*http.Response, error) {
    req, _ := http.NewRequest("GET", apiBase+"/self/me?api-version=1.0", nil)
    req.Header.Set("Authorization", "Bearer "+token)
    return http.DefaultClient.Do(req)
}

func main() {
    clientID := os.Getenv("CSIQ_CLIENT_ID")
    if clientID == "" {
        log.Fatal("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide")
    }
    clientSecret := os.Getenv("CSIQ_CLIENT_SECRET")
    if clientSecret == "" {
        log.Fatal("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide")
    }

    // 1. Connectivity (no auth).
    health, err := http.Get(apiBase + "/health")
    if err != nil {
        log.Fatal(err)
    }
    body, _ := io.ReadAll(health.Body)
    health.Body.Close()
    fmt.Println(string(body))

    // 2. Authenticate.
    token, err := getToken(clientID, clientSecret)
    if err != nil {
        log.Fatal(err)
    }

    // 3 + 4. Call, and on 401 refresh (re-request) and retry once.
    res, err := call(token)
    if err != nil {
        log.Fatal(err)
    }
    if res.StatusCode == http.StatusUnauthorized {
        res.Body.Close()
        if token, err = getToken(clientID, clientSecret); err != nil {
            log.Fatal(err)
        }
        if res, err = call(token); err != nil {
            log.Fatal(err)
        }
    }
    defer res.Body.Close()
    out, _ := io.ReadAll(res.Body)
    fmt.Println(res.StatusCode, string(out))
}

Signing in as a person instead

For a user-facing tool, swap step 2 for the device flow and step 4 for the refresh_token grant. Steps 1 and 3 (connectivity and the authenticated call) are identical; only how you obtain and renew the token changes.

Next