-
Notifications
You must be signed in to change notification settings - Fork 2
API Clients
Auto-generated REST client libraries for the TMI threat modeling API in multiple languages.
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
-
Python --
tmi_clientpackage (most mature, includes bug fixes). Available on PyPI. -
TypeScript --
@tmiclient/clientnpm 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.
- 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
The Python client is the primary focus with modern packaging and bug fixes.
pip install tmi-clientpip install git+https://github.com/ericfitz/tmi-clients.git#subdirectory=python-client-generatedcd python-client-generated
uv syncfrom __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}")The Python client has been patched to properly handle diagram operations.
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)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)# 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)}")The API uses separate schemas for input and output operations:
-
Input schemas (
*Inputclasses): 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) vsDfdDiagramInput(input) - Other resources following the same convention
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)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)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"}}
}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=termTest 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_diagramKey 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
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.
npm install @tmiclient/clientnpm install ericfitz/tmi-clients#subdirectory=typescript-client-generatedimport {
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);
}
}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 |
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);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,
});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);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);
}
}The Go client provides idiomatic Go interfaces for the TMI API.
go get github.com/ericfitz/tmi-clients/go-client-generatedimport (
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)
}All clients provide access to the complete TMI API:
- 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
- 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)
- 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)
- Custom metadata on all resources
- Bulk metadata operations
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.
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).
List endpoints support pagination with query parameters:
-
limit- Number of items per page -
offset- Number of items to skip
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)All API errors use a consistent format with error and error_description fields:
{
"error": "not_found",
"error_description": "Threat model not found"
}For information on regenerating clients from the OpenAPI spec, see API-Client-Maintenance.
- API-Overview -- Complete API documentation
- REST-API-Reference -- Endpoint reference
- API-Integration -- Integration patterns
- API-Workflows -- Common workflows
- API-Client-Maintenance -- Client regeneration and maintenance
- Using TMI for Threat Modeling
- Accessing TMI
- Authentication
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS, OCI, GCP, Azure)
- Deploying TMI Server
- OCI Container Deployment
- Certificate Automation
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions