Forge API
Connect your AI agent to any machine. Clean, normalized, governed data in, supervised action out.
Quick start
- Get your API key: free at /pricing (keyless Forge Connect sandbox)
- Identify a machine:
POST /v1/identify - Send data:
POST /v1/normalize - Connect a tool:
POST /v1/tools - Set up automation:
POST /v1/triggers/natural - Query history:
GET /v1/history/{machine_id} - Tamper-evident record:
POST /v1/settle
All /v1/* requests need Authorization: Bearer fnet_… and (where applicable) Content-Type: application/json.
Endpoints
/v1/identify
Provision or look up a persistent, verified identity (machine_id) for one machine. Idempotent on (oem, model, serial).
{
"oem": "fanuc",
"model": "Robodrill α-D21MiB5",
"serial": "R30iB-12345",
"site": "Line 3"
}
{
"machine_id": "FORGE-4de03b",
"internal_id": "fanuc:m-20ia:r30ib-12345",
"created": true,
"machine": { "status": "active" },
"first_seen": "2026-05-04T01:00:00Z"
}
/v1/normalize
Translate raw OEM telemetry to canonical FCS data. Auto-provisions identity from oem/model/serial when no machine_id is given. Returns triggers_fired[] when any active trigger matches. Optional idempotency_key dedups retries within 24h. Optional observed_at (ISO 8601 controller clock) is stored alongside the server ingested_at and used for sustained-trigger windows.
{
"data": { "Spindle_Speed": 2200, "spindle_load": 92 },
"oem": "fanuc",
"model": "Robodrill α-D21MiB5",
"serial": "R30iB-12345",
"observed_at": "2026-05-05T09:30:15Z",
"idempotency_key": "optional-caller-supplied-key"
}
{
"normalized": { "spindle_speed_rpm": 2200, "spindle_load_pct": 92 },
"field_mappings": { "spindle_load": { "mapping_id": "6c34…", "canonical": "spindle_load_pct", "confidence": 0.93 } },
"fields_total": 2,
"fields_renamed": 2, // source != canonical
"fields_identity": 0, // source == canonical
"fields_unknown": 0, // no mapping
"coverage_pct": 100,
"machine_id": "FORGE-4de03b",
"history_id": "c419…",
"idempotency_key": "ab12…",
"observed_at": "2026-05-05T09:30:15+00:00",
"ingested_at": "2026-05-05T09:30:18+00:00",
"deduplicated": false,
"triggers_fired": [{ "trigger_id": "7f0c…", "trigger_name": "high spindle load", "actual_value": 92 }]
}
If a retry hits the same idempotency_key within 24h, response includes deduplicated: true + the original history_id, and trigger evaluation is skipped. If |observed_at − ingested_at| > 60s, response includes clock_skew_warning.
/v1/tools
Register a webhook endpoint as a tool. Triggers reference these by tool_id. auth_secret returned ONCE on create; never read back via GET.
{
"name": "slack-maintenance",
"url": "https://hooks.slack.com/services/T…/B…/xxx",
"method": "POST",
"payload_template": { "text": "Alert: {{oem}} {{model}}, {{field}} at {{value}}" },
"auth_type": "none"
}
{ "id": "add2…", "name": "slack-maintenance", "auth_type": "none", "enabled": true }
/v1/triggers/natural
Parse a natural-language instruction into a structured trigger. NEVER auto-activates; returns parsed_trigger for review and explicit POST to /v1/triggers.
{ "machine_id": "FORGE-4de03b", "instruction": "Alert maintenance Slack when spindle load exceeds 90." }
{
"parsed_trigger": {
"machine_id": "FORGE-4de03b",
"name": "Spindle Load Alert",
"condition": { "field": "spindle_load_pct", "op": ">", "value": 90 },
"actions": [{ "tool_id": "add2…" }]
},
"confirmation_required": true
}
/v1/history/{machine_id}
Per-machine canonical history. Query params: from, to (ISO-8601), fields (csv), limit (≤1000), summary (bool).
GET /v1/history/FORGE-4de03b?limit=5&summary=true
{
"machine_id": "FORGE-4de03b",
"machine": { "oem": "fanuc", "model": "Robodrill α-D21MiB5", "site": "Line 3" },
"row_count": 2,
"avg_coverage": 100,
"fields_covered": ["spindle_load_pct", "spindle_speed_rpm", "sensor_readings.coolant_temp"]
}
/v1/settle
Create a tamper-evident, independently auditable record. Every event is hash-chained into the machine's verified history. Add ?batch=true with body {"machine_id": "…"} to roll all unsettled events for that machine into one record.
POST /v1/settle?batch=true
{ "machine_id": "FORGE-4de03b" }
{
"machine_id": "FORGE-4de03b",
"record_hash": "413d5ec…",
"event_count": 4,
"chain_index": 128,
"verify_url": "https://forge.foundrynet.io/v1/attestation/FORGE-4de03b"
}
The verified history is exportable for lessors, insurers, and auditors. Optional off-network anchoring is available on Enterprise.
MCP server
Connect any MCP-compatible client (Claude, Cursor, custom agents, LangChain, CrewAI, AutoGen). One line:
claude mcp add --transport http forge https://mcp.foundrynet.io/mcp --header 'Authorization: Bearer YOUR_API_KEY'
Endpoint: https://mcp.foundrynet.io/mcp (Streamable HTTP). Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"forge": {
"command": "npx",
"args": ["mcp-remote", "https://mcp.foundrynet.io/mcp"]
}
}
}
Listed on Smithery, Glama, and the MCP directory.
MCP tools (29 total)
identify_machine, provision/lookup machine_id from (oem, model, serial)normalize_telemetry, raw OEM data → canonical FCS, with auto-provision + trigger evalquery_machine_history, time-series + summary over canonical_datacreate_automation, natural language → parsed_trigger (review-then-activate)activate_automation, arm a parsed_trigger after user confirmationlist_automations, show all triggers configured for a machinedisable_automation, pause a trigger without deleting it (reversible)delete_automation, soft delete (30-day restorable);?permanent=truefor hard deletequery_webhook_history, webhook delivery history for a trigger (HTTP status codes + timing)restore_automation, undo a soft delete within 30 daysverify_record, create a tamper-evident record (single payload OR batched)fire_sandbox, demo the full watch→fire→verify loop against a built-in sandbox machinecorrect_mapping, teach Forge the right canonical field for a source columnget_coverage, ask what Forge can normalize before you send data (recognized OEMs + fields)predict, forecast the next N readings of a canonical telemetry seriespredict_breach, predict whether and when a series will cross a thresholdremaining_life, estimate remaining useful life before a failure thresholdpredict_batch, run predictions for an entire fleet in one callfleet_health, roll a fleet of predictions into one health dashboarddetect_anomalies, flag anomalies without a full forecast (z-score + IQR)machine_intelligence, normalize + predict + assess in one call from raw telemetryprediction_accuracy, report how well past predictions matched reality
Plus operational capability tools, calculate_oee, energy_report, shift_report, diagnose, and parts_prediction, for 29 tools total. All run on the same normalized data layer. Works with any MCP-compatible client.
Prediction input contract
The forecasting tools, predict, predict_breach, and remaining_life, take the canonical series as a time_series array (not values), ordered oldest to newest, and require a minimum of 16 data points. The canonical_field must be a recognized canonical, call get_coverage to list them.
POST /v1/predict_breach
{
"canonical_field": "spindle_load_pct",
"time_series": [60, 61, 62, 63, 64, 66, 67, 68, 70, 71, 73, 74, 76, 78, 80, 82],
"threshold": 95,
"direction": "above",
"horizon": 24
}
fleet_health and predict_batch take a fleet payload and are POST, not GET. Fewer than 16 points, or an unrecognized canonical field, returns the 422 envelope shown below.
Reliability + record integrity
Soft delete
DELETE /v1/triggers/{id} defaults to soft delete, sets deleted_at, hides from list/eval, restorable for 30 days via PATCH /v1/triggers/{id}/restore. Add ?permanent=true for irrecoverable hard delete.
Idempotency
/v1/normalize accepts an optional idempotency_key. If absent, the server synthesizes sha256(machine_id + canonical(data))[:32]. Repeats within 24h return the original response with deduplicated: true and skip trigger evaluation. Telemetry gateways: set this on retry.
Coverage metric
Every /v1/normalize response carries three counters: fields_renamed (source != canonical), fields_identity (source == canonical), fields_unknown (no mapping). coverage_pct = (renamed + identity) / total. Identity mappings, like parts_count → parts_count at confidence 1.0, count toward coverage.
Record timestamps
observed_at (ISO 8601 from the controller clock) is optional on /v1/normalize. Stored alongside ingested_at (server). When the difference exceeds 60s, the response includes clock_skew_warning. Sustained-condition trigger windows reference observed_at when present so windows are anchored to when events actually happened, not when they arrived.
Structured errors
Every /v1/* validation failure returns the same envelope:
{
"error": true,
"status": 422,
"field": "body.oem",
"reason": "Field required",
"received_value": {},
"suggestion": "e.g. 'fanuc'",
"additional_errors": [ { "field": "body.model", "reason": "Field required" } ]
}
System requirements
The Forge kernel runs as a Docker container on any Linux host: cloud, on-premise server, industrial PC, or edge gateway.
Minimum (edge deployment)
CPU: 2 cores, x86_64 or ARM64
RAM: 4 GB
Storage: 10 GB available
OS: Linux (Ubuntu 20.04+, Debian 11+, Amazon Linux 2)
Network: Ethernet to OT network (Modbus / OPC UA / SNMP / MTConnect / EtherNet/IP / PROFINET / BACnet / ROS2 / J1939 / MAVLink)
Optional internet for cloud sync and updates
Runtime: Docker 20.10+ or AWS Greengrass V2
Recommended (production)
CPU: 4 cores
RAM: 8 GB
Storage: 50 GB (accommodates extended telemetry history)
Network: 1 Gbps to OT network + internet for CMMS and updates
Cloud deployment
Any container host: Railway, AWS ECS, GCP Cloud Run, Azure Container Apps. No special hardware. Connects to equipment via API or protocol bridge.
Resource profile
Container image: ~2.2 GB (compressed ~900 MB)
Runtime memory: ~1.1 GB RSS steady state, ~1.5 GB peak
CPU at idle: < 5% single core
CPU during predict: ~40% single core for 2-3 seconds
Bandwidth: < 1 MB/hour per machine at 1 reading/second
What is in the 1.1 GB
Normalization engine (BGE-M3 embeddings): ~600 MB
Prediction engine (TimesFM): ~400 MB
Application + vertical packs + schema: ~100 MB
Supported protocols
MQTT / Sparkplug B (push, real-time streaming)
Modbus RTU / TCP (poll, legacy PLCs and sensors)
OPC UA (subscribe, modern controllers)
SNMP (poll, servers and network equipment)
MTConnect (poll, CNC controllers: Haas, Mazak, Okuma, DMG Mori)
EtherNet/IP (CIP) (poll, Allen-Bradley / Rockwell PLCs)
PROFINET / S7 (poll, Siemens S7-300/400/1200/1500)
BACnet/IP (poll, building automation: BMS, HVAC, meters)
ROS2 / DDS (subscribe, robots and AMR fleets)
J1939 / CAN (listen, engines and heavy equipment)
MAVLink (listen, drones and autonomous vehicles)
REST API (push or pull, any HTTP-capable device)
CSV upload (batch, historical data import)
No new hardware required
Forge connects to your existing sensors, PLCs, and controllers through the protocols they already speak. No proprietary gateways, no sensor replacements, no wiring changes.
Pricing
Metered billing via Stripe. Counts roll up to your subscription's current billing period and settle on Stripe's normal cycle. Query GET /v1/usage for live counts and an estimated bill.
POST /v1/normalize, 1¢ per call- Trigger fire (per webhook delivery, only on 2xx), 5¢
POST /v1/settle?batch=true, 2¢ per batch verified (skipped verifications are free)GET /v1/history/{machine_id}, 1¢ per queryPOST /v1/identify, freePOST /v1/triggers/natural, free (setup step; revenue comes from the trigger fires it produces)POST /v1/feedback/{mapping_id}/..., free- Trigger evaluation (the eval pass during
/v1/normalize), free; you only pay for fires that successfully deliver
Get an API key via POST /v1/keys with a Stripe payment method (email + payment_method_id from Stripe.js).