{
  "service": "dwg2dxf-engine",
  "node": "office-DESKTOP-FR9GT2I",
  "engine": "dwg2dxf 0.14",
  "endpoints": {
    "GET /health": "liveness check (includes which node answered)",
    "POST /convert": "body = DWG file bytes (raw binary or multipart/form-data field 'file'); returns the converted DXF. Requires API key via X-Api-Key header or ?key=. Optional query: as=r2000..r2018, minimal=1, verify=1. Optional header X-Filename. Max 512 MB \u2014 for bigger files use /convert-url.",
    "POST /convert-url": "JSON {source_url, filename?, as?, minimal?, upload_url?, verify?, async?}. The engine downloads the DWG itself (up to 20 GB, e.g. a SharePoint/Graph download URL), converts, then PUTs the DXF to upload_url (a Graph upload session) if given, else returns the DXF bytes. async:true returns a job id immediately \u2014 poll GET /jobs/<id>; without upload_url fetch the DXF from GET /jobs/<id>/result. Requires API key.",
    "POST /measure": "Server-side takeoff statistics \u2014 per-layer polyline/line/arc lengths, entity counts, INSERT block counts, units ($INSUNITS), extents. Input: JSON {source_url, filename?, top?, layers?, include_blocks?, async?} or raw DWG bytes (query: top, layers). Returns KB of JSON instead of a 100 MB DXF. Lengths are DRAWING UNITS \u2014 convert per wardrobe rules before pricing. Requires API key.",
    "GET /jobs/<id>": "async job status + result JSON (API key required)",
    "GET /jobs/<id>/result": "async convert output DXF, one-shot (API key required)",
    "WARNING": "Do NOT use as=r2000 if the DXF will be parsed programmatically (ezdxf etc.): LibreDWG's r2000 downconversion emits broken linked-entity sequences. Omit 'as' (native version) \u2014 that output parses cleanly. Use verify=1 to prove it."
  },
  "features": "gzip responses (Accept-Encoding: gzip); truncation detection (fetch Content-Length check; X-DXF-SHA256/X-DXF-Bytes/X-DXF-Complete on output)",
  "example": "curl --compressed -X POST -H 'X-Api-Key: <key>' --data-binary @plan.dwg <base-url>/convert -o plan.dxf"
}