Skip to content

API Clients

Eric Fitzgerald edited this page Apr 6, 2026 · 1 revision

API Clients

Auto-generated REST client libraries for the TMI threat modeling API in multiple languages.

Overview

The TMI API Clients repository contains auto-generated client libraries for interacting with the TMI REST API. These clients provide strongly-typed interfaces for working with threat models, diagrams, assets, threats, and other TMI resources in your preferred programming language.

Repository: github.com/ericfitz/tmi-clients

Available Languages

  • Python -- tmi_client package (most mature, includes bug fixes). Available on PyPI.
  • TypeScript -- @tmiclient/client npm package. Available on npm.
  • Go -- Go module (github.com/ericfitz/tmi-clients/go-client-generated)

All clients are auto-generated from the TMI OpenAPI specification.

Features

  • Full REST API coverage for all TMI endpoints
  • Strongly-typed models matching OpenAPI schemas
  • Built-in request/response handling
  • OAuth 2.0 authentication with PKCE support (Bearer JWT tokens)
  • AntV X6 graph library compatibility for diagrams
  • Comprehensive API documentation

Python Client

The Python client is the primary focus with modern packaging and bug fixes.

Installation

From PyPI (recommended)

pip install tmi-client

From Git

pip install git+https://github.com/ericfitz/tmi-clients.git#subdirectory=python-client-generated

Using uv

cd python-client-generated
uv sync

Quick Start

from __future__ import print_function
import time
import tmi_client
from tmi_client.rest import ApiException
from pprint import pprint

# Configure API client
configuration = tmi_client.Configuration()
configuration.host = "https://tmi.example.com"
configuration.api_key['bearerAuth'] = 'YOUR_JWT_TOKEN'
configuration.api_key_prefix['bearerAuth'] = 'Bearer'

# Create API instance
api_client = tmi_client.ApiClient(configuration)
api_instance = tmi_client.ThreatModelsApi(api_client)

try:
    # List threat models
    threat_models = api_instance.list_threat_models()
    pprint(threat_models)
except ApiException as e:
    print(f"Exception when calling ThreatModelsApi: {e}")

Working with Diagrams

The Python client has been patched to properly handle diagram operations.

Creating a Diagram

from tmi_client.models.create_diagram_request import CreateDiagramRequest

# Create diagram request
request = CreateDiagramRequest(
    name="My Data Flow Diagram",
    type="DFD-1.0.0"
)

# Create the diagram
api = tmi_client.ThreatModelSubResourcesApi(api_client)
diagram = api.create_threat_model_diagram(request, threat_model_id)

Updating a Diagram

Important: Use DfdDiagramInput for updates, NOT DfdDiagram:

from tmi_client.models.dfd_diagram_input import DfdDiagramInput

# Correct - DfdDiagramInput for updates (no readOnly fields)
update = DfdDiagramInput(
    type="DFD-1.0.0",
    name="Updated Diagram Name",
    cells=[
        {
            "id": "node-1",
            "shape": "process",
            "x": 100,
            "y": 100,
            "width": 120,
            "height": 60,
            "attrs": {
                "body": {"fill": "#E1F5FE"},
                "text": {"text": "Web Server"}
            }
        }
    ]
)

updated_diagram = api.update_threat_model_diagram(update, tm_id, diagram_id)

Reading a Diagram

# Returns DfdDiagram (with readOnly fields: id, created_at, modified_at)
diagram = api.get_threat_model_diagram(threat_model_id, diagram_id)
print(f"Diagram: {diagram.name}")
print(f"Created: {diagram.created_at}")
print(f"Cells: {len(diagram.cells)}")

Input vs Output Schemas

The API uses separate schemas for input and output operations:

  • Input schemas (*Input classes): Used for POST/PUT operations, exclude readOnly fields
  • Output schemas (base classes): Returned from GET operations, include all fields including readOnly

This pattern applies to:

  • DfdDiagram (output) vs DfdDiagramInput (input)
  • Other resources following the same convention

Working with Assets

Asset type must be one of: data, hardware, software, infrastructure, service, personnel.

from tmi_client.models.asset_input import AssetInput

# Create an asset
asset = AssetInput(
    name="User Database",
    type="software",
    description="PostgreSQL database storing user data"
)

created_asset = api.create_threat_model_asset(asset, threat_model_id)

Working with Threats

The name and threat_type fields are required. Use asset_id (a single UUID) to associate a threat with an asset.

from tmi_client.models.threat_input import ThreatInput

# Create a threat
threat = ThreatInput(
    name="SQL Injection",
    threat_type=["Tampering"],
    description="Attacker could inject SQL commands",
    severity="high",
    status="open",
    asset_id=asset_id  # single UUID, not a list
)

created_threat = api.create_threat_model_threat(threat, threat_model_id)

Cell Structure for Diagrams

Diagrams use the AntV X6 graph library format for cells (nodes and edges):

# Node example
node = {
    "id": "node-uuid",
    "shape": "process",
    "x": 100,
    "y": 100,
    "width": 120,
    "height": 60,
    "attrs": {
        "body": {"fill": "#E1F5FE"},
        "text": {"text": "Web Component"}
    }
}

# Edge example
edge = {
    "id": "edge-uuid",
    "shape": "edge",
    "source": {"cell": "source-node-id"},
    "target": {"cell": "target-node-id"},
    "attrs": {"line": {"stroke": "#333"}}
}

Testing

Run tests using pytest:

cd python-client-generated

# Run all tests
uv run --with pytest python3 -m pytest test/ -v

# Run specific test file
uv run --with pytest python3 -m pytest test/test_dfd_diagram.py -v

# Run with coverage
uv run --with pytest --with pytest-cov python3 -m pytest test/ --cov=tmi_client --cov-report=term

Multi-version Testing with Tox

Test against Python 3.9-3.14:

cd python-client-generated

# Test all versions
tox

# Test specific version
tox -e py311

# Run with pytest arguments
tox -- -k test_dfd_diagram

API Classes

Key API classes for common operations:

  • ThreatModelsApi -- Threat model CRUD operations
  • ThreatModelSubResourcesApi -- Sub-resource operations (diagrams, assets, threats, documents, notes, repositories) and metadata
  • ThreatsApi -- Threat-specific bulk operations (bulk PATCH, bulk DELETE)
  • AssetsApi -- Asset-specific operations
  • DocumentsApi -- Document management
  • NotesApi -- Notes and documentation
  • RepositoriesApi -- Source code repository references
  • AuthenticationApi -- OAuth2 authentication flows
  • CollaborationApi -- Diagram collaboration sessions
  • WebSocketApi -- WebSocket ticket generation
  • AuditTrailApi -- Audit trail queries
  • WebhooksApi -- Webhook subscription management
  • ProjectsApi -- Project management
  • TeamsApi -- Team management
  • UserAccountApi -- User profile and preferences

TypeScript Client

The TypeScript client is generated with the typescript-fetch generator from openapi-generator. It uses the Fetch API and provides full type safety with TypeScript interfaces and models.

Installation

From npm (recommended)

npm install @tmiclient/client

From Git

npm install ericfitz/tmi-clients#subdirectory=typescript-client-generated

Quick Start

import {
  Configuration,
  ThreatModelsApi,
  ResponseError,
} from "@tmiclient/client";

// Configure API client
const config = new Configuration({
  basePath: "https://tmi.example.com",
  accessToken: "YOUR_JWT_TOKEN",
});

// Create API instance
const api = new ThreatModelsApi(config);

// List threat models
try {
  const response = await api.listThreatModels({});
  console.log("Threat models:", response);
} catch (error) {
  if (error instanceof ResponseError) {
    console.error("API error:", error.response.status);
  }
}

Configuration

The Configuration class accepts the following parameters:

Parameter Type Description
basePath string Base URL of the TMI API
accessToken string | Promise<string> | Function OAuth2 Bearer token or a function that returns one
apiKey string | Promise<string> | Function API key for apiKey-based authentication
headers Record<string, string> Additional headers to include on every request
credentials RequestCredentials Fetch credentials mode (omit, same-origin, include)
fetchApi FetchAPI Custom fetch implementation (defaults to global fetch)
middleware Middleware[] Request/response middleware chain

Working with Diagrams

import {
  Configuration,
  ThreatModelSubResourcesApi,
  type CreateDiagramRequest,
  type DfdDiagramInput,
} from "@tmiclient/client";

const config = new Configuration({
  basePath: "https://tmi.example.com",
  accessToken: "YOUR_JWT_TOKEN",
});

const api = new ThreatModelSubResourcesApi(config);

// Create a diagram
const diagramRequest: CreateDiagramRequest = {
  name: "My Data Flow Diagram",
  type: "DFD-1.0.0",
};

const diagram = await api.createThreatModelDiagram({
  threatModelId: threatModelId,
  createDiagramRequest: diagramRequest,
});

console.log("Created diagram:", diagram.id);

Updating a Diagram

Use DfdDiagramInput for updates, which excludes readOnly fields:

import type { DfdDiagramInput } from "@tmiclient/client";

const update: DfdDiagramInput = {
  type: "DFD-1.0.0",
  name: "Updated Diagram",
  cells: [
    {
      id: "node-1",
      shape: "process",
      x: 100,
      y: 100,
      width: 120,
      height: 60,
      attrs: {
        body: { fill: "#E1F5FE" },
        text: { text: "Web Server" },
      },
    },
  ],
};

const updated = await api.updateThreatModelDiagram({
  threatModelId: threatModelId,
  diagramId: diagramId,
  dfdDiagramInput: update,
});

Using Middleware

You can add middleware to intercept requests and responses. This is useful for logging, authentication refresh, or error handling:

import { Configuration, ThreatModelsApi, type Middleware } from "@tmiclient/client";

const loggingMiddleware: Middleware = {
  async pre(context) {
    console.log(`Request: ${context.init.method} ${context.url}`);
    return context;
  },
  async post(context) {
    console.log(`Response: ${context.response.status}`);
    return context.response;
  },
};

const config = new Configuration({
  basePath: "https://tmi.example.com",
  accessToken: "YOUR_JWT_TOKEN",
  middleware: [loggingMiddleware],
});

const api = new ThreatModelsApi(config);

Error Handling

The client throws ResponseError for non-2xx responses and FetchError for network failures:

import { ResponseError, FetchError } from "@tmiclient/client";

try {
  const model = await api.getThreatModel({ threatModelId: "invalid-id" });
} catch (error) {
  if (error instanceof ResponseError) {
    console.error("HTTP status:", error.response.status);
    const body = await error.response.json();
    console.error("Error:", body.error, body.error_description);
  } else if (error instanceof FetchError) {
    console.error("Network error:", error.cause.message);
  }
}

Go Client

The Go client provides idiomatic Go interfaces for the TMI API.

Installation

go get github.com/ericfitz/tmi-clients/go-client-generated

Usage

import (
    tmi "github.com/ericfitz/tmi-clients/go-client-generated"
)

// Configure client
cfg := tmi.NewConfiguration()
cfg.Host = "tmi.example.com"
cfg.Scheme = "https"

// Add authentication
cfg.AddDefaultHeader("Authorization", "Bearer YOUR_JWT_TOKEN")

// Create API client
client := tmi.NewAPIClient(cfg)

// List threat models
models, _, err := client.ThreatModelsApi.ListThreatModels(context.Background())
if err != nil {
    log.Fatal(err)
}

API Endpoints

All clients provide access to the complete TMI API:

Core Resources

  • Threat Models -- CRUD operations
  • Diagrams -- DFD diagram management
  • Assets -- Asset tracking
  • Threats -- Threat management with bulk operations
  • Documents -- Document attachments
  • Notes -- Markdown notes
  • Repositories -- Source code references

Authentication

  • OAuth 2.0 authorization with PKCE (/oauth2/authorize, /oauth2/callback, /oauth2/token)
  • Token refresh (/oauth2/refresh) and introspection (/oauth2/introspect)
  • Token revocation (/oauth2/revoke)
  • SAML authentication (/saml/{provider}/login)
  • User profile management (/me)

Collaboration

  • Diagram collaboration sessions (/threat_models/{id}/diagrams/{id}/collaborate)
  • WebSocket connections for real-time editing (/threat_models/{id}/diagrams/{id}/ws)
  • Notification WebSocket (/ws/notifications)

Metadata

  • Custom metadata on all resources
  • Bulk metadata operations

Common Patterns

Hierarchical Authorization

TMI uses hierarchical authorization -- access control is defined at the ThreatModel level via the authorization field (readers, writers, owners). All child resources inherit permissions from their parent ThreatModel.

Bulk Operations

The following resources support bulk operations:

  • Threats -- POST (create), PUT (upsert), PATCH (partial update), DELETE (batch delete)
  • Assets -- POST (create), PUT (upsert)
  • Documents -- POST (create), PUT (upsert)
  • Repositories -- POST (create), PUT (upsert)

Notes and Diagrams do NOT support bulk operations.

All resources support bulk metadata operations (POST, PUT, PATCH).

Pagination

List endpoints support pagination with query parameters:

  • limit - Number of items per page
  • offset - Number of items to skip

Partial Updates (PATCH)

All resources support PATCH for partial updates using JSON Patch (RFC 6902). The request must use content type application/json-patch+json:

# Create JSON Patch document (a list of operations)
patch = [
    {"op": "replace", "path": "/name", "value": "New Name"},
    {"op": "add", "path": "/description", "value": "New description"}
]

# Apply patch to a threat model
api = tmi_client.ThreatModelsApi(api_client)
api.patch_threat_model(patch, threat_model_id)

Error Responses

All API errors use a consistent format with error and error_description fields:

{
  "error": "not_found",
  "error_description": "Threat model not found"
}

Next Steps

For information on regenerating clients from the OpenAPI spec, see API-Client-Maintenance.

Related Pages

Clone this wiki locally