> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aviatrix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Learn the basic API request pattern, authentication flow, and session handling for Aviatrix Controller APIs.

The Controller API gives you programmatic access to Controller operations such
as configuration updates, status checks, and automation workflows.

## Choose the Right Interface

Use the API when you need imperative control from scripts, CI pipelines, or
external platforms.

In most Infrastructure as Code workflows, prefer Terraform first. Terraform
provides declarative state management, repeatable plans, and safer drift
handling. Use direct API calls when Terraform does not cover a required
operation or when you need ad hoc or event-driven actions.

## Typical API Workflow

Many API integrations follow this order:

1. Authenticate with `POST /v2/api/login`.
2. Capture the returned `CID` session token.
3. Send authenticated requests with `CID` in the `Authorization` header as
   `cid <CID>`.
4. Re-authenticate and refresh `CID` after session expiration.

This page walks through that common pattern and provides a reusable example.

## Prerequisites

* A Controller URL or IP address.
* A user account with permissions for the endpoints you call.

## API Request Pattern

<Steps>
  <Step title="Authenticate with the login API">
    Send `POST /v2/api/login` with `username` and `password`.

    ```bash theme={null}
    CONTROLLER="controller.example.com"
    USERNAME="api-user"
    PASSWORD="replace-with-password"

    LOGIN_RESPONSE=$(curl -sk -X POST "https://${CONTROLLER}/v2/api/login" \
      -H "Content-Type: application/json" \
      -d "{\"username\":\"${USERNAME}\",\"password\":\"${PASSWORD}\"}")
    ```
  </Step>

  <Step title="Store the CID from the login response">
    Extract the session token and store it for subsequent requests.

    ```bash theme={null}
    CID=$(echo "$LOGIN_RESPONSE" | jq -r '.CID // .results.CID // empty')

    if [[ -z "$CID" ]]; then
      echo "Login failed: no CID returned"
      exit 1
    fi
    ```
  </Step>

  <Step title="Call API endpoints with the CID">
    Send `CID` in the `Authorization` header as `cid <CID>`.

    ```bash theme={null}
    # Example request
    curl -sk -X GET "https://${CONTROLLER}/v2/api/list_accounts" \
      -H "Authorization: cid ${CID}"
    ```
  </Step>

  <Step title="Refresh the session when needed">
    If a request returns `"return": false`, check `"reason"` and log in again to get a new `CID` before retrying.
  </Step>
</Steps>

## Response Format

Most v2 endpoints use a standard response shape:

* `return`: `true` on success, `false` on failure.
* `results`: response payload on success.
* `reason`: failure reason when `return` is `false`.

## Example Flows

<Tabs>
  <Tab title="Bash">
    ```bash theme={null}
    #!/usr/bin/env bash
    set -euo pipefail

    CONTROLLER="controller.example.com"
    USERNAME="api-user"
    PASSWORD="replace-with-password"

    login() {
      local login_response
      login_response=$(curl -sk -X POST "https://${CONTROLLER}/v2/api/login" \
        -H "Content-Type: application/json" \
        -d "{\"username\":\"${USERNAME}\",\"password\":\"${PASSWORD}\"}")

      CID=$(echo "$login_response" | jq -r '.CID // .results.CID // empty')
      if [[ -z "$CID" ]]; then
        echo "Unable to obtain CID from login response" >&2
        echo "$login_response" >&2
        exit 1
      fi
    }

    post_with_cid() {
      local endpoint="$1"

      curl -sk -X GET "https://${CONTROLLER}${endpoint}" \
        -H "Authorization: cid ${CID}"
    }

    login
    response=$(post_with_cid "/v2/api/list_accounts")

    if [[ "$(echo "$response" | jq -r '.return // false')" != "true" ]]; then
      echo "Session may be invalid or expired. Re-authenticating..."
      login
      response=$(post_with_cid "/v2/api/list_accounts")
    fi

    echo "$response" | jq
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    #!/usr/bin/env python3
    import json
    import requests

    CONTROLLER = "controller.example.com"
    USERNAME = "api-user"
    PASSWORD = "replace-with-password"
    BASE_URL = f"https://{CONTROLLER}"


    def login() -> str:
      r = requests.post(
        f"{BASE_URL}/v2/api/login",
        json={"username": USERNAME, "password": PASSWORD},
        verify=False,
        timeout=30,
      )
      r.raise_for_status()
      data = r.json()
      cid = data.get("CID") or data.get("results", {}).get("CID")
      if not cid:
        raise RuntimeError(f"Login did not return CID: {data}")
      return cid


    def get_with_cid(endpoint: str, cid: str) -> dict:
      r = requests.get(
        f"{BASE_URL}{endpoint}",
        headers={"Authorization": f"cid {cid}"},
        verify=False,
        timeout=30,
      )
      r.raise_for_status()
      return r.json()


    cid = login()
    response = get_with_cid("/v2/api/list_accounts", cid)

    if not response.get("return", False):
      cid = login()
      response = get_with_cid("/v2/api/list_accounts", cid)

    print(json.dumps(response, indent=2))
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
      "bytes"
      "crypto/tls"
      "encoding/json"
      "fmt"
      "net/http"
      "time"
    )

    const (
      controller = "controller.example.com"
      username   = "api-user"
      password   = "replace-with-password"
    )

    var baseURL = "https://" + controller

    func main() {
      client := &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
          TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
      }

      // Login to get CID
      loginBody, _ := json.Marshal(map[string]string{
        "username": username,
        "password": password,
      })
      loginReq, _ := http.NewRequest(http.MethodPost, baseURL+"/v2/api/login", bytes.NewBuffer(loginBody))
      loginReq.Header.Set("Content-Type", "application/json")

      loginResp, err := client.Do(loginReq)
      if err != nil {
        panic(err)
      }
      defer loginResp.Body.Close()

      var loginData map[string]any
      if err := json.NewDecoder(loginResp.Body).Decode(&loginData); err != nil {
        panic(err)
      }

      cid, _ := loginData["CID"].(string)
      if cid == "" {
        if results, ok := loginData["results"].(map[string]any); ok {
          cid, _ = results["CID"].(string)
        }
      }
      if cid == "" {
        panic(fmt.Sprintf("login did not return CID: %v", loginData))
      }

      // Call API with Authorization header
      req, _ := http.NewRequest(http.MethodGet, baseURL+"/v2/api/list_accounts", nil)
      req.Header.Set("Authorization", "cid "+cid)

      resp, err := client.Do(req)
      if err != nil {
        panic(err)
      }
      defer resp.Body.Close()

      var response map[string]any
      if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
        panic(err)
      }

      b, _ := json.MarshalIndent(response, "", "  ")
      fmt.Println(string(b))
    }
    ```
  </Tab>
</Tabs>
