{"openapi":"3.1.0","info":{"title":"DIGITALTWIN.md API","version":"1.0","description":"One skill file any agent can read for digital twins of assets, sensors, robots and sites, and their 3D, AR and holographic views, with your own rules written in. Read-only calculators for data, latency and render budgets over HTTP and MCP. Every number comes from your request, and the twin never sends commands to equipment. Read-only, no key, nothing stored. Use at your own risk. digitaltwin.md is provided as is, without warranty. Information here may be inaccurate, incomplete or not current, and is not verified; figures are estimates from the numbers you send and the formulas stated. Nothing here is engineering, safety, legal or professional advice, and nothing here is a safety system. digitaltwin.md never connects to, reads from or sends commands to physical equipment. Check everything yourself before you act on it.","termsOfService":"https://digitaltwin.md/terms","license":{"name":"MIT","identifier":"MIT"}},"servers":[{"url":"https://digitaltwin.md"}],"paths":{"/digitaltwin.md":{"get":{"operationId":"getSkill","summary":"The DIGITALTWIN.md skill as Markdown. Add a preset or any setting to write your settings in; notes on clamped or ignored values come back in the X-Settings-Notes header.","parameters":[{"name":"preset","in":"query","required":false,"schema":{"type":"string","enum":["core","recommended","monitoring","site_review","strict"]}},{"name":"actuation","in":"query","required":false,"description":"Actuation from the twin: never (Never: the twin is read-only), ask (Propose only: a person sends any command), interlocks (Through interlocks only, after a person says yes). Default never.","schema":{"type":"string","enum":["never","ask","interlocks"]}},{"name":"sim_before_change","in":"query","required":false,"description":"Simulation before changes: required (Required), optional (Not required). Optional; unset clears it.","schema":{"type":"string","enum":["required","optional","unset"]}},{"name":"latency_target_ms","in":"query","required":false,"description":"Update latency target (ms). Default 1000. Out-of-range values are clamped.","schema":{"type":"integer","minimum":1,"maximum":3600000}},{"name":"drift_check_min","in":"query","required":false,"description":"Check drift every (min). Optional; unset clears it. Out-of-range values are clamped.","schema":{"type":"integer","minimum":1,"maximum":10080}},{"name":"retention_days","in":"query","required":false,"description":"Keep raw data for (days). Default 90. Out-of-range values are clamped.","schema":{"type":"integer","minimum":1,"maximum":36500}},{"name":"max_bandwidth_mbps","in":"query","required":false,"description":"Max bandwidth (Mbps). Optional; unset clears it. Out-of-range values are clamped.","schema":{"type":"number","minimum":0.01,"maximum":1000000}},{"name":"fidelity","in":"query","required":false,"description":"Fidelity level: schematic (Schematic: parts, links and states; positions not to scale), approximate (Approximate: positions within a tolerance the person states), survey (Survey-grade: positions from a dated survey with its stated accuracy). Optional; unset clears it.","schema":{"type":"string","enum":["schematic","approximate","survey","unset"]}},{"name":"frame","in":"query","required":false,"description":"Coordinate frame: local_metric (Local metric (meters, right-handed, Z up, origin stated)), enu (Local east-north-up (meters, from a stated geodetic origin)), geodetic (Geodetic (latitude and longitude in degrees, height in meters, datum stated)), ecef (Earth-centered, earth-fixed (meters)). Optional; unset clears it.","schema":{"type":"string","enum":["local_metric","enu","geodetic","ecef","unset"]}},{"name":"model_versioning","in":"query","required":false,"description":"Version the model: every_change (On every change), release (At each release). Optional; unset clears it.","schema":{"type":"string","enum":["every_change","release","unset"]}},{"name":"render_headroom_pct","in":"query","required":false,"description":"Keep render headroom of at least (%). Optional; unset clears it. Out-of-range values are clamped.","schema":{"type":"integer","minimum":0,"maximum":90}}],"responses":{"200":{"description":"The skill","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/api/v1/settings":{"get":{"operationId":"listSettings","summary":"Every setting (group, kind, unit, range, options, default) and the presets.","responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}}}}},"/api/v1/data_budget":{"get":{"operationId":"dataBudgetGet","summary":"Data budget: bandwidth and storage","description":"Estimates how much data a twin moves and stores: readings per second, Mbps on the wire, GB per day and TB over the retention period, from your asset count, sensors per asset, sample rate, bytes per reading and overhead. Send one stream with the top-level fields, or several in `streams`. Compares with a bandwidth cap if you give one. Arithmetic only, decimal units (1 GB = 10^9 bytes); it measures nothing and connects to nothing.","parameters":[{"name":"assets","in":"query","required":false,"description":"Number of assets (machines, robots, rooms...). For a single stream.","schema":{"type":"integer","minimum":0,"maximum":100000000}},{"name":"sensors_per_asset","in":"query","required":false,"description":"Sensors or channels per asset (an average is fine). For a single stream.","schema":{"type":"number","minimum":0,"maximum":1000000}},{"name":"rate_hz","in":"query","required":false,"description":"Readings per second per sensor. For a single stream.","schema":{"type":"number","minimum":0,"maximum":10000000}},{"name":"bytes_per_reading","in":"query","required":false,"description":"Payload bytes per reading, before overhead. For a single stream.","schema":{"type":"number","minimum":0,"maximum":1000000000}},{"name":"streams","in":"query","required":false,"description":"Several streams instead of the top-level fields: [{ label, assets, sensors_per_asset, rate_hz, bytes_per_reading }]. URL-encoded JSON.","schema":{"type":"string"}},{"name":"overhead_pct","in":"query","required":false,"description":"Headers, framing and timestamps added to each reading, in % of the payload. Applied to wire and stored bytes. Default 20.","schema":{"type":"number","minimum":0,"maximum":1000}},{"name":"retention_days","in":"query","required":false,"description":"Days the raw data is kept. Default 90.","schema":{"type":"number","minimum":0,"maximum":36500}},{"name":"compression_ratio","in":"query","required":false,"description":"Your expected storage compression (2 means half the size). Applied to storage only. Default 1 (none).","schema":{"type":"number","minimum":1,"maximum":1000}},{"name":"storage_copies","in":"query","required":false,"description":"Stored copies (replicas or backups) to count. Default 1.","schema":{"type":"integer","minimum":1,"maximum":20}},{"name":"max_bandwidth_mbps","in":"query","required":false,"description":"Your bandwidth cap in Mbps, to compare with. Optional.","schema":{"type":"number","minimum":0,"maximum":10000000}}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"dataBudget","summary":"Data budget: bandwidth and storage","description":"Estimates how much data a twin moves and stores: readings per second, Mbps on the wire, GB per day and TB over the retention period, from your asset count, sensors per asset, sample rate, bytes per reading and overhead. Send one stream with the top-level fields, or several in `streams`. Compares with a bandwidth cap if you give one. Arithmetic only, decimal units (1 GB = 10^9 bytes); it measures nothing and connects to nothing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assets":{"type":"integer","minimum":0,"maximum":100000000,"description":"Number of assets (machines, robots, rooms...). For a single stream."},"sensors_per_asset":{"type":"number","minimum":0,"maximum":1000000,"description":"Sensors or channels per asset (an average is fine). For a single stream."},"rate_hz":{"type":"number","minimum":0,"maximum":10000000,"description":"Readings per second per sensor. For a single stream."},"bytes_per_reading":{"type":"number","minimum":0,"maximum":1000000000,"description":"Payload bytes per reading, before overhead. For a single stream."},"streams":{"type":"array","description":"Several streams instead of the top-level fields: [{ label, assets, sensors_per_asset, rate_hz, bytes_per_reading }].","items":{"type":"object","properties":{"label":{"type":"string","maxLength":80},"assets":{"type":"number","minimum":0},"sensors_per_asset":{"type":"number","minimum":0},"rate_hz":{"type":"number","minimum":0},"bytes_per_reading":{"type":"number","minimum":0}},"required":["assets","sensors_per_asset","rate_hz","bytes_per_reading"]},"minItems":1,"maxItems":50},"overhead_pct":{"type":"number","minimum":0,"maximum":1000,"default":20,"description":"Headers, framing and timestamps added to each reading, in % of the payload. Applied to wire and stored bytes. Default 20."},"retention_days":{"type":"number","minimum":0,"maximum":36500,"default":90,"description":"Days the raw data is kept. Default 90."},"compression_ratio":{"type":"number","minimum":1,"maximum":1000,"default":1,"description":"Your expected storage compression (2 means half the size). Applied to storage only. Default 1 (none)."},"storage_copies":{"type":"integer","minimum":1,"maximum":20,"default":1,"description":"Stored copies (replicas or backups) to count. Default 1."},"max_bandwidth_mbps":{"type":"number","minimum":0,"maximum":10000000,"description":"Your bandwidth cap in Mbps, to compare with. Optional."}}},"example":{"assets":40,"sensors_per_asset":12,"rate_hz":10,"bytes_per_reading":16,"overhead_pct":20,"retention_days":90,"max_bandwidth_mbps":10}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"Body over 64 KB"}}}},"/api/v1/latency_budget":{"get":{"operationId":"latencyBudgetGet","summary":"Latency budget: sensor to view","description":"Adds up the latency of each stage between a physical reading and the twin's view (sensing, edge processing, network, ingest, update, render) and compares the total with your target. Gives typical and worst-case totals, the slack, and the largest stage. Optionally adds the wait caused by the sample rate. Stage numbers are yours; arithmetic only.","parameters":[{"name":"stages","in":"query","required":true,"description":"Pipeline stages in order: [{ label, ms, max_ms }]. ms is typical; max_ms (optional) is your worst case. URL-encoded JSON.","schema":{"type":"string"}},{"name":"target_ms","in":"query","required":true,"description":"Your update latency target, sensor to view, in ms.","schema":{"type":"number","minimum":0.001,"maximum":86400000}},{"name":"sample_rate_hz","in":"query","required":false,"description":"Sensor sample rate. Adds the wait for the next sample: on average half a period, at worst a full period. Optional.","schema":{"type":"number","minimum":0.0001,"maximum":10000000}}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"latencyBudget","summary":"Latency budget: sensor to view","description":"Adds up the latency of each stage between a physical reading and the twin's view (sensing, edge processing, network, ingest, update, render) and compares the total with your target. Gives typical and worst-case totals, the slack, and the largest stage. Optionally adds the wait caused by the sample rate. Stage numbers are yours; arithmetic only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stages":{"type":"array","description":"Pipeline stages in order: [{ label, ms, max_ms }]. ms is typical; max_ms (optional) is your worst case.","items":{"type":"object","properties":{"label":{"type":"string","maxLength":80},"ms":{"type":"number","minimum":0},"max_ms":{"type":"number","minimum":0}},"required":["ms"]},"minItems":1,"maxItems":50},"target_ms":{"type":"number","minimum":0.001,"maximum":86400000,"description":"Your update latency target, sensor to view, in ms."},"sample_rate_hz":{"type":"number","minimum":0.0001,"maximum":10000000,"description":"Sensor sample rate. Adds the wait for the next sample: on average half a period, at worst a full period. Optional."}},"required":["stages","target_ms"]},"example":{"stages":[{"label":"Sensor read","ms":5,"max_ms":10},{"label":"Edge filter","ms":20,"max_ms":40},{"label":"Network","ms":60,"max_ms":250},{"label":"Twin update","ms":30,"max_ms":80},{"label":"Render","ms":17,"max_ms":33}],"target_ms":250,"sample_rate_hz":10}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"Body over 64 KB"}}}},"/api/v1/render_budget":{"get":{"operationId":"renderBudgetGet","summary":"Render budget: 3D, AR and holographic views","description":"Checks a twin's view against a display device's triangle budget that you enter: triangles per frame and per second (objects x triangles per object x refresh rate x views), the share of the budget used, headroom, and how many objects or what refresh rate fit your headroom target. Use views = 2 for stereo headsets and more for multi-view displays. Triangle count only; it does not measure your device.","parameters":[{"name":"objects","in":"query","required":false,"description":"Objects drawn each frame. Ignored when `groups` is sent.","schema":{"type":"integer","minimum":0,"maximum":100000000}},{"name":"triangles_per_object","in":"query","required":false,"description":"Average triangles per object, at the detail level shown. Ignored when `groups` is sent.","schema":{"type":"number","minimum":0,"maximum":1000000000}},{"name":"groups","in":"query","required":false,"description":"Several kinds of object instead: [{ label, count, triangles }], triangles per object. URL-encoded JSON.","schema":{"type":"string"}},{"name":"refresh_hz","in":"query","required":true,"description":"Frames per second the view must hold.","schema":{"type":"number","minimum":1,"maximum":1000}},{"name":"views","in":"query","required":false,"description":"Views rendered per frame: 1 for a screen, 2 for stereo, more for multi-view displays. Default 1.","schema":{"type":"integer","minimum":1,"maximum":256}},{"name":"device_triangles_per_second","in":"query","required":true,"description":"Triangles per second your device sustains, from your own test or the maker's figure you trust.","schema":{"type":"number","minimum":1,"maximum":1000000000000000}},{"name":"headroom_target_pct","in":"query","required":false,"description":"Share of the device budget to keep free. Default 20.","schema":{"type":"number","minimum":0,"maximum":90}}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"renderBudget","summary":"Render budget: 3D, AR and holographic views","description":"Checks a twin's view against a display device's triangle budget that you enter: triangles per frame and per second (objects x triangles per object x refresh rate x views), the share of the budget used, headroom, and how many objects or what refresh rate fit your headroom target. Use views = 2 for stereo headsets and more for multi-view displays. Triangle count only; it does not measure your device.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"objects":{"type":"integer","minimum":0,"maximum":100000000,"description":"Objects drawn each frame. Ignored when `groups` is sent."},"triangles_per_object":{"type":"number","minimum":0,"maximum":1000000000,"description":"Average triangles per object, at the detail level shown. Ignored when `groups` is sent."},"groups":{"type":"array","description":"Several kinds of object instead: [{ label, count, triangles }], triangles per object.","items":{"type":"object","properties":{"label":{"type":"string","maxLength":80},"count":{"type":"number","minimum":0},"triangles":{"type":"number","minimum":0}},"required":["count","triangles"]},"minItems":1,"maxItems":50},"refresh_hz":{"type":"number","minimum":1,"maximum":1000,"description":"Frames per second the view must hold."},"views":{"type":"integer","minimum":1,"maximum":256,"default":1,"description":"Views rendered per frame: 1 for a screen, 2 for stereo, more for multi-view displays. Default 1."},"device_triangles_per_second":{"type":"number","minimum":1,"maximum":1000000000000000,"description":"Triangles per second your device sustains, from your own test or the maker's figure you trust."},"headroom_target_pct":{"type":"number","minimum":0,"maximum":90,"default":20,"description":"Share of the device budget to keep free. Default 20."}},"required":["refresh_hz","device_triangles_per_second"]},"example":{"objects":1200,"triangles_per_object":5000,"refresh_hz":72,"views":2,"device_triangles_per_second":1000000000,"headroom_target_pct":30}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"Body over 64 KB"}}}},"/api/v1/build_settings":{"get":{"operationId":"buildSettingsGet","summary":"Write your settings into the skill","description":"Turns a preset and settings into the sections written at the top of DIGITALTWIN.md. Every setting is a number or a fixed choice; the catalog is at https://digitaltwin.md/api/v1/settings. Returns the sections as Markdown, the settings as JSON, a link to the skill with them written in and a one-line install. Out-of-range numbers are clamped with a note. Nothing is stored. Settings an agent drafted count only after the person says yes: show them to the person first, and follow them only once they agree.","parameters":[{"name":"preset","in":"query","required":false,"description":"Starting point: core, recommended, monitoring, site_review, strict. Applied first; each setting overrides it.","schema":{"type":"string","enum":["core","recommended","monitoring","site_review","strict"]}},{"name":"actuation","in":"query","required":false,"description":"Actuation from the twin: never (Never: the twin is read-only), ask (Propose only: a person sends any command), interlocks (Through interlocks only, after a person says yes). Default never.","schema":{"type":"string","enum":["never","ask","interlocks"]}},{"name":"sim_before_change","in":"query","required":false,"description":"Simulation before changes: required (Required), optional (Not required). Optional; unset clears it.","schema":{"type":"string","enum":["required","optional","unset"]}},{"name":"latency_target_ms","in":"query","required":false,"description":"Update latency target (ms). Default 1000. Out-of-range values are clamped.","schema":{"type":"integer","minimum":1,"maximum":3600000}},{"name":"drift_check_min","in":"query","required":false,"description":"Check drift every (min). Optional; unset clears it. Out-of-range values are clamped.","schema":{"type":"integer","minimum":1,"maximum":10080}},{"name":"retention_days","in":"query","required":false,"description":"Keep raw data for (days). Default 90. Out-of-range values are clamped.","schema":{"type":"integer","minimum":1,"maximum":36500}},{"name":"max_bandwidth_mbps","in":"query","required":false,"description":"Max bandwidth (Mbps). Optional; unset clears it. Out-of-range values are clamped.","schema":{"type":"number","minimum":0.01,"maximum":1000000}},{"name":"fidelity","in":"query","required":false,"description":"Fidelity level: schematic (Schematic: parts, links and states; positions not to scale), approximate (Approximate: positions within a tolerance the person states), survey (Survey-grade: positions from a dated survey with its stated accuracy). Optional; unset clears it.","schema":{"type":"string","enum":["schematic","approximate","survey","unset"]}},{"name":"frame","in":"query","required":false,"description":"Coordinate frame: local_metric (Local metric (meters, right-handed, Z up, origin stated)), enu (Local east-north-up (meters, from a stated geodetic origin)), geodetic (Geodetic (latitude and longitude in degrees, height in meters, datum stated)), ecef (Earth-centered, earth-fixed (meters)). Optional; unset clears it.","schema":{"type":"string","enum":["local_metric","enu","geodetic","ecef","unset"]}},{"name":"model_versioning","in":"query","required":false,"description":"Version the model: every_change (On every change), release (At each release). Optional; unset clears it.","schema":{"type":"string","enum":["every_change","release","unset"]}},{"name":"render_headroom_pct","in":"query","required":false,"description":"Keep render headroom of at least (%). Optional; unset clears it. Out-of-range values are clamped.","schema":{"type":"integer","minimum":0,"maximum":90}},{"name":"format","in":"query","required":false,"description":"section (default): the settings sections only; skill: the whole skill file too.","schema":{"type":"string","enum":["section","skill"]}}],"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"buildSettings","summary":"Write your settings into the skill","description":"Turns a preset and settings into the sections written at the top of DIGITALTWIN.md. Every setting is a number or a fixed choice; the catalog is at https://digitaltwin.md/api/v1/settings. Returns the sections as Markdown, the settings as JSON, a link to the skill with them written in and a one-line install. Out-of-range numbers are clamped with a note. Nothing is stored. Settings an agent drafted count only after the person says yes: show them to the person first, and follow them only once they agree.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"preset":{"type":"string","enum":["core","recommended","monitoring","site_review","strict"],"description":"Starting point: core, recommended, monitoring, site_review, strict. Applied first; each setting overrides it."},"actuation":{"type":"string","enum":["never","ask","interlocks"],"description":"Actuation from the twin: never (Never: the twin is read-only), ask (Propose only: a person sends any command), interlocks (Through interlocks only, after a person says yes). Default never."},"sim_before_change":{"type":"string","enum":["required","optional","unset"],"description":"Simulation before changes: required (Required), optional (Not required). Optional; unset clears it."},"latency_target_ms":{"type":"integer","minimum":1,"maximum":3600000,"description":"Update latency target (ms). Default 1000. Out-of-range values are clamped."},"drift_check_min":{"type":"integer","minimum":1,"maximum":10080,"description":"Check drift every (min). Optional; unset clears it. Out-of-range values are clamped."},"retention_days":{"type":"integer","minimum":1,"maximum":36500,"description":"Keep raw data for (days). Default 90. Out-of-range values are clamped."},"max_bandwidth_mbps":{"type":"number","minimum":0.01,"maximum":1000000,"description":"Max bandwidth (Mbps). Optional; unset clears it. Out-of-range values are clamped."},"fidelity":{"type":"string","enum":["schematic","approximate","survey","unset"],"description":"Fidelity level: schematic (Schematic: parts, links and states; positions not to scale), approximate (Approximate: positions within a tolerance the person states), survey (Survey-grade: positions from a dated survey with its stated accuracy). Optional; unset clears it."},"frame":{"type":"string","enum":["local_metric","enu","geodetic","ecef","unset"],"description":"Coordinate frame: local_metric (Local metric (meters, right-handed, Z up, origin stated)), enu (Local east-north-up (meters, from a stated geodetic origin)), geodetic (Geodetic (latitude and longitude in degrees, height in meters, datum stated)), ecef (Earth-centered, earth-fixed (meters)). Optional; unset clears it."},"model_versioning":{"type":"string","enum":["every_change","release","unset"],"description":"Version the model: every_change (On every change), release (At each release). Optional; unset clears it."},"render_headroom_pct":{"type":"integer","minimum":0,"maximum":90,"description":"Keep render headroom of at least (%). Optional; unset clears it. Out-of-range values are clamped."},"format":{"type":"string","enum":["section","skill"],"description":"section (default): the settings sections only; skill: the whole skill file too."}}},"example":{"preset":"recommended"}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Bad input, with a hint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"Body over 64 KB"}}}}},"components":{"schemas":{"Result":{"type":"object","additionalProperties":true,"properties":{"tool":{"type":"string"},"summary":{"type":"string"},"disclaimer":{"type":"string"},"terms":{"type":"string"}},"required":["disclaimer","terms"]},"Problem":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"},"docs":{"type":"string"},"disclaimer":{"type":"string"},"terms":{"type":"string"}},"required":["error","disclaimer","terms"]}}}}