The CADLens API turns DWG, DXF, and DWF files into structured JSON your code can render, diff, and route.
This is a REST API. Authenticate with a bearer API key, upload a file, poll the job until it finishes (or wait for a webhook), then read a JSON document with vector entities, layers, drawing metadata, and a rendered PNG preview.
All endpoints live under the /v1 path prefix. Stable formats: DWG, DXF, DWF. Beta formats: PDF, DWFx, DGN (V7). File size limit is 10 MB on the free plan, and 25 MB–250 MB depending on your paid plan (1 GB on Enterprise). The file type is verified by magic bytes, not the extension.
Create a free account, then create and copy your cadl_… key in the dashboard — the full key is shown only once. Stash it in your environment as CADLENS_KEY. See Authentication for the rules.
Multipart upload to /v1/parse with a file field (up to your plan limit — 10 MB on free). The response is a job — in the default async mode its status is PENDING until the worker picks it up.
Either poll GET /v1/jobs/{jobId} until status === "COMPLETED", or skip polling and use webhooks. Then GET /v1/jobs/{jobId}/result to read the parsed JSON and preview URL.
!
Upload limit is 10 MB on the free plan (higher on paid plans). Accepted formats: DWG, DXF, DWF (stable) and PDF, DWFx, DGN V7 (beta). See errors for the codes returned when a file is rejected.
Every request to the parse and jobs endpoints uses a bearer API key in the Authorization header:
Authorization: Bearer cadl_<48 hex characters>
Keys are created in the dashboard and start with the prefix cadl_. The full key is shown once at creation — store it securely; only the first few characters (the key prefix) are visible afterwards. Revoking a key in the dashboard takes effect immediately. A request with a missing, malformed, revoked, or expired key returns 401.
!
Never call CADLens from a browser. Anything you POST from fetch() in the front-end leaks your key. Proxy through your backend.
CADLens speaks the Model Context Protocol. Point an MCP client — Claude Code, Claude Desktop, or anything else that speaks MCP — at CADLens and the agent can parse a DWG, DXF, or DWF file, inspect its layers, count entities, and pull a preview image on its own. No glue code, no SDK, no polling loop for you to write.
The tools wrap the same REST API documented on this page and enforce the same authentication, ownership checks, and plan quotas. An MCP call and a POST /v1/parse are billed identically.
The hosted endpoint needs nothing installed. It uses the Streamable HTTP transport and is stateless — every request carries its own auth, so there is no session to establish and no initialize handshake required.
POST https://api.cadlens.co/mcp
Authenticate with the same cadl_ API key you use for REST, in the Authorization: Bearer header. Requests must send Content-Type: application/json and Accept: application/json, text/event-stream — an MCP client does this for you. A missing or revoked key returns 401 with a JSON-RPC error body and a WWW-Authenticate: Bearer header. The endpoint accepts POST only; anything else returns 405.
cadlens-mcp is a small Node package that runs on your machine and talks to the CADLens API over HTTPS. Use it when the agent needs to read CAD files straight off the local filesystem — the local server accepts a filePath, which a hosted server cannot. It requires Node.js 20+ and a CADLENS_API_KEY environment variable.
i
Which one should you use? Prefer the remote server — it is always current and there is nothing to update. Reach for the local one only for local file access, or to point at a self-hosted API via CADLENS_API_BASE.
!
Your API key is a credential. An agent configured with your key can parse, list, and delete jobs on your account. Issue a dedicated key for agent use so you can revoke it without disturbing your production integration.
Connect a client
# Remote — nothing to install
claude mcp add --transport http cadlens \
https://api.cadlens.co/mcp \
--header "Authorization: Bearer cadl_•••••"
# Local — runs on your machine
claude mcp add cadlens \
--env CADLENS_API_KEY=cadl_••••• \
-- npx -y cadlens-mcp
Seven tools, identical on the remote and local servers. Every result comes back as a single JSON text block. Tool failures are returned as content with isError: true — they never tear down the connection, so an agent can read the error and retry.
Tool
Description
parse_cad_file
Upload a DWG, DXF, or DWF file and parse it. Waits for completion by default and returns the drawing summary.
get_job_status
Check a job: PENDING, PROCESSING, COMPLETED, or FAILED, plus timing and any error message.
get_job_result
Retrieve the parsed vector JSON. Defaults to a compact summary — see the modes below.
get_job_image
Get pre-signed PNG preview URLs, one per sheet. URLs expire after about an hour.
list_jobs
List recent jobs for the authenticated account, newest first, with an optional status filter.
delete_job
Delete a job and its artifacts. This cannot be undone.
get_usage
Current billing period: plan, quota, requests used, remaining, and renewal date.
A single drawing can hold tens of thousands of entities — far more than fits in a model's context window. get_job_result therefore defaults to a summary and lets the agent drill down from there.
Mode
Returns
summary
default
File info, per-sheet and per-layer counts, layer names and colours, bounding boxes. No geometry.
entities_by_type
Entities matching entityType (e.g. LINE, CIRCLE, TEXT), capped at 500.
entities_on_layer
Entities on the exact layerName, capped at 500.
full
The complete payload, uncapped. Can be very large.
Pass sheet — a sheet key from the summary — to narrow any filtered mode to a single sheet or layout. When a filter matches more than 500 entities the response says so, withmatchedCount, returnedCount, and truncated, so the agent knows to narrow further.
Two prompts, ready to copy. They teach an agent how to use CADLens properly — which call to make first, how to keep a 40,000-entity drawing out of its context window, and what to do when a parse fails. Pick the one that matches how your agent reaches us.
Use
When
MCP
The agent has the CADLens MCP server connected and calls tools by name. Nothing to write — the tools already exist.
API key
The agent has an API key and can make HTTP requests, but no MCP. It writes the curl or fetch calls itself.
Both are written as SKILL.md documents, so they drop straight into .claude/skills/cadlens/SKILL.md. They work equally well pasted into a system prompt, a Cursor rule, or the top of a chat — remove the --- frontmatter if the target does not use it.
For an agent with the CADLens MCP server connected.
---
name: cadlens-cad-parsing
description: Parse DWG, DXF, and DWF drawings into structured vector JSON, layers, and PNG previews using the CADLens MCP tools.
---
# CADLens — parse CAD drawings (MCP)
Use the CADLens MCP tools whenever the task touches a CAD drawing: reading its
geometry, listing layers, counting entities, measuring extents, or producing a
preview image. Never try to decode a DWG, DXF, or DWF file yourself — the binary
formats are proprietary and text extraction gives wrong answers.
## Tools
- `parse_cad_file` — submit a drawing. Returns the summary directly when
`wait` is true (the default). Billable.
- `get_job_status` — PENDING, PROCESSING, COMPLETED, or FAILED, plus timing
and any error message.
- `get_job_result` — the parsed data. Read "Result size" below before calling.
- `get_job_image` — pre-signed PNG preview URLs, one per sheet. They expire
after about an hour.
- `list_jobs` — recent jobs on the account, newest first, optional status filter.
- `delete_job` — deletes the job and its artifacts. Irreversible. Ask the user first.
- `get_usage` — plan, quota, requests used, remaining, renewal date.
## Submitting a file
Pass exactly one source. Both, or neither, is an error.
- `fileUrl` — a public https URL the server downloads. Preferred for anything
large; up to 100 MB. It must resolve to a publicly routable host, and
redirects are not followed.
- `fileBase64` + `fileName` — inline bytes, up to 20 MB decoded. `fileName`
must carry the real extension, e.g. `"plan.dwg"`.
- `filePath` — local stdio server only, which reads straight off disk.
Leave `wait` at its default. The remote server waits up to 90 seconds (the
local one up to 5 minutes); if the parse runs longer you get a job ID back
instead — poll `get_job_status` until it is terminal, then read the result.
## Result size — read this before calling get_job_result
One drawing can hold tens of thousands of entities. Asking for all of them will
exhaust your context and answer nothing.
1. Call `get_job_result` with no `mode`. It defaults to `summary`: file
info, per-sheet and per-layer counts, layer names and colours, and bounding
boxes — no geometry.
2. Decide from that summary which sheet, and which layer or entity type,
actually answers the question.
3. Call again with `mode: "entities_on_layer"` and `layerName`, or
`mode: "entities_by_type"` and `entityType` (LINE, LWPOLYLINE, TEXT,
INSERT, CIRCLE, ARC, …). Add `sheet` — a sheet key from the summary — to
narrow to a single sheet or layout.
4. A filtered call returns at most 500 entities. When the response reports
`truncated: true`, compare `matchedCount` with `returnedCount` and
narrow further — by sheet first, then by layer.
Reach for `mode: "full"` only when the user explicitly asked for the complete
payload. It is uncapped and can be very large.
## Errors and limits
- Tool failures come back as content with `isError: true`. Read the message
and correct the call — the connection stays up, so retry rather than give up.
- 60 requests per minute against the remote server.
- A parse counts as one request against the plan quota, exactly as a REST
`POST /v1/parse` would. Failed parses are never billed.
- DWG, DXF, and DWF are stable. PDF, DWFx, and DGN V7 are beta — say so if the
user's file is one of those. DGN V8 is not supported.
- Call `get_usage` before a batch of parses if quota might be tight.
## Reporting back
Lead with what the drawing is: format, units, sheet count, extents. Quote the
real numbers from the summary instead of describing them vaguely. Units come
from `file.units` — never state a measurement without them. When a picture
would help, call `get_job_image` and hand the user the URL, noting that it
expires in about an hour.
i
The whole point of the “result size” rules in both prompts is that a single drawing can carry tens of thousands of entities. An agent that asks for all of them exhausts its context and still cannot answer the question. Summary first, then drill down — keep that part even if you rewrite the rest.
For Claude Code, save the prompt as .claude/skills/cadlens/SKILL.md in your project — it is picked up on the next run, with no registration step. Claude Desktop and Cursor take the same body; Cursor wants its own frontmatter keys instead of ours. For anything else, paste it into the system prompt.
Agents that discover CADLens on their own do not need any of this. The same skills are served machine-readably at /.well-known/agent-skills/index.json, each with a SHA-256 digest — see Agent discovery.
!
Issue a separate key for agent use. Both prompts let an agent parse, list, and delete jobs on your account. A dedicated key can be revoked without touching your production integration. The prompts tell the agent to read the key from the environment and never print it — do not paste the key into the prompt itself.
Install as a skill
# Paste the prompt into a skill file
mkdir -p .claude/skills/cadlens
$EDITOR .claude/skills/cadlens/SKILL.md
# Claude picks it up on the next run —
# no restart, no registration step.
One-off prompts
Parse this drawing with the CADLens MCP tools: <path or https URL>.
Start with get_job_result in summary mode, tell me the format, units, sheet
count, and overall extents, then list the layers with their entity counts.
Do not pull full geometry unless I ask.
An agent that has never heard of CADLens can find everything it needs without a human in the loop. These endpoints are public, unauthenticated, and cacheable.
MCP server card
/.well-known/mcp/server-card.json — the machine-readable description of this MCP server: both transports, the auth scheme, and every tool. Served from cadlens.co and api.cadlens.co, and advertised in a Link header with rel="mcp-server-card".
Agent skills
/.well-known/agent-skills/index.json — four SKILL.md manifests (parse-cad-file, extract-cad-layers, cad-preview-image, and cadlens-mcp for tool-based clients), each with a SHA-256 digest so a client can verify what it loaded. The same prompts are on this page under Skill prompts, ready to copy.
API catalog
/.well-known/api-catalog — an RFC 9727 linkset pointing at the docs, the OpenAPI description, the Postman collection, and the health endpoint.
llms.txt
/llms.txt is a short brief on what CADLens is and which endpoints exist; /llms-full.txt is the long form, with schema, pricing, and worked examples. Both are plain text with no markup to strip.
Markdown pages
Send Accept: text/markdown and pages return markdown instead of HTML — the same content, without the DOM.
WebMCP
Browser agents get in-page tools on navigator.modelContext for searching these docs, reading pricing, and navigating the site — no server round trip.
Crawling is welcome. robots.txt allows search, AI answering, and model training for named AI agents, and declares it explicitly with a Content-Signal directive.
Discovery endpoints
# MCP server card
/.well-known/mcp/server-card.json
# Agent skills (SKILL.md manifests)
/.well-known/agent-skills/index.json
# API catalog (RFC 9727 linkset)
/.well-known/api-catalog
# Plain-text context for LLMs
/llms.txt
/llms-full.txt
Upload a CAD file as multipart/form-data. Every parse returns both vector JSON and a rendered PNG preview. In the default async mode the response is 202 with a job_id and status PENDING — poll GET /v1/jobs/{jobId} or use a webhook until the job is COMPLETED.
Accepted formats: DWG, DXF, DWF (stable) and PDF, DWFx, DGN V7 (beta). Maximum file size depends on your plan — 10 MB on the free tier, up to 100 MB on paid plans. The type is verified by magic bytes, not the file extension.
Per-call webhook to POST when the job completes or fails. Overrides any saved endpoint.
mode
optional
enum
`async` returns 202 + job_id immediately. `sync` holds the request until the worker finishes — but large files (≥10 MB, or drawings that convert to very large geometry) return 202 immediately with a `message` field and continue processing asynchronously; poll the job or use a webhook. Prefer `async` for large files. · default async
Read the current status of a job. Idempotent and not counted against your quota — safe to poll. status is one of PENDING, PROCESSING, COMPLETED, or FAILED. imageUrl is a signed PNG URL once COMPLETED, otherwise null.
Returns the parsed document as a sheets[] array. Each sheet contains its own entities (geometry), layers (only those used on that sheet), a signed imageUrl, and spatial stats (boundingBox, area, perimeter). Top-level file and summary fields give file metadata and aggregate counts. Available only once the job is COMPLETED — calling it earlier returns 400 "Job result is not yet available".
Returns a JSON object with imageUrl — a signed S3 URL to the rendered PNG preview, valid for one hour. The same URL is also included in the job and result responses. Available only once the job is COMPLETED.
Permanently deletes the job and its stored artifacts (vector JSON and the rendered PNG). Returns 204 No Content on success, or 404 if the job does not exist for your API key. This cannot be undone.
A worked example of a single parse, end to end. Follow the three steps alongside — upload a file, poll the job until it's COMPLETED, then read the result.
Upload.POST /v1/parse with your file returns a job_id and status PENDING.
Poll.GET /v1/jobs/{jobId} until the status is COMPLETED (or FAILED).
Read.GET /v1/jobs/{jobId}/result returns the vector JSON, layers, metadata, and a preview URL.
i
This is a read-only example. To run requests interactively against your own account, open the Playground in your dashboard after creating an API key.
Polling. Call GET /v1/jobs/{jobId} until status is COMPLETED or FAILED. Use exponential backoff starting around 250ms and capped at ~4s — the example alongside does this. Polling does not count against your usage quota. Prefer a webhook if you'd rather not poll.
When a job is COMPLETED, its result is organised as a list of sheets. Each sheet carries its own entities, layers, bounding box, area, and preview image URL — so multi-sheet DWG/DXF files are fully navigable without any client-side filtering. Top-level file and summary fields give file-level metadata and aggregate counts at a glance.
File-level metadata: original filename, format (DWG/DXF/DWF), CAD version string, and drawing units.
summary
Aggregate counts across all sheets: totalSheets, totalEntities, totalLayers, overall bounding box, and whether the result was truncated.
sheets
Array of layout/sheet objects. Each sheet has its own entities (geometry) and layers (only those used on that sheet), a signed imageUrl, and spatial stats (boundingBox, area, perimeter). See Vector entity types.
Layer
A named group entities belong to, with a colour, line type, and visibility — mirroring the source CAD layers.
Metadata
Drawing-level information carried for backward compatibility: file name, source format, drawing units, and overall extent.
Bounding box
The rectangular extent of a sheet (or the whole drawing in summary) — width and height in drawing units.
Units
The drawing's measurement unit — millimetres, centimetres, metres, inches, feet, or unknown.
Preview image
A rendered PNG per sheet, returned as a signed URL valid for one hour.
For the exact field names and types, see the Get result reference.
Each entry in sheets[n].entities holds one geometric object from that sheet, wrapped in the Schema v2 envelope: identity (id, handle, type, category, layer, layout), the spatial data in geometry (2D points in the drawing's own units, original precision), and always-present computed helpers bbox and metrics (rounded to 6 decimals, null where not applicable). text is populated for TEXT/MTEXT and reference.blockName for INSERT — both are null on other types.
Skip polling. Pass webhookUrl when you call /v1/parse, or save an endpoint in the dashboard (up to two per account). CADLens then POSTs a JSON event as the job progresses and finishes. The request carries User-Agent: CADLens-Webhook/1.0 and a JSON body.
The result is ready — the event carries lightweight per-sheet metadata (entity/layer counts, bounding box, area, perimeter) and preview URLs, plus resultUrl to fetch the full parsed JSON geometry.
job.failed
The parse failed — the event carries the failure reason.
Your endpoint should respond 2xx within 10 seconds. A non-2xx (or timeout) is retried up to 3 times with exponential backoff (~2s, 4s, 8s, capped at 30s); after that the delivery is recorded as exhausted.
Deliveries to a saved endpoint are signed. Each request carries X-CADLens-Signature: t=<ts>,v1=<hmac> and a X-CADLens-Timestamp header. Recompute the HMAC-SHA256 of <timestamp>.<raw request body> using your endpoint's signing secret (shown once when you create the endpoint) and compare it to the v1 value; reject requests whose timestamp is too old to stop replays.
i
As a belt-and-suspenders practice, on job.completed re-fetch GET /v1/jobs/{jobId}/result with your API key to read the authoritative result.
Errors return a flat JSON object with a human-readable error message and, sometimes, a programmatic code. Request-validation failures use a slightly richer shape — { "error": "Validation error", "details": { fieldErrors, formErrors } }. The HTTP status carries the category.
`No file provided. Upload a file with field name "file".`
400
optional
—
`File header does not match a supported CAD format. Allowed: DWG/DXF/DWF/DGN/DWFX/PDF`
400
optional
—
`DGN V8 files are not supported. Only Bentley DGN V7 is currently accepted.`
400
optional
—
`Job result is not yet available` — the job is not COMPLETED yet.
413
optional
—
`File exceeds your plan's upload limit` — the per-upload cap depends on your plan (10 MB–250 MB, 1 GB on Enterprise).
429
optional
—
`Monthly limit of N requests reached for PLAN plan`
429
optional
DUPLICATE_FILE_WARNING
The same filename was re-uploaded too quickly, or on a regular bot-like schedule. Response includes `attempt` and `maxAttempts`; the `maxAttempts`-th violation escalates to `DUPLICATE_FILE_BLOCKED`.
429
optional
DUPLICATE_FILE_BLOCKED
Uploads of that filename are temporarily blocked after repeated violations. Response includes `retryAfterSeconds` and a `Retry-After` header; other filenames are unaffected.
400
optional
FILE_TOO_COMPLEX
The converted drawing is too large or intricate to process within the memory/size limit.
400
optional
CONVERSION_FAILED
The underlying CAD-to-DXF conversion step failed or timed out.
400
optional
CORRUPT_FILE
The file could not be read or parsed — its contents are malformed or unreadable.
400
optional
EMPTY_DRAWING
The drawing contains no extractable entities.
—
optional
PARSE_TIMEOUT
Parsing exceeded the server-side time limit — the job is marked FAILED; retry or contact support for very large drawings.
—
optional
FILE_REUSED_ACROSS_ACCOUNTS
Risk signal (not a request-blocking error): the same file fingerprint was also uploaded from other accounts, contributing to abuse-risk scoring.
Usage is metered as a monthly quota per plan. Each successful parse counts as one request; failed parses and read calls (get job, result, image, list) do not count. The counter resets at the start of each calendar month (UTC).
Plan
Parses / mo
Max file
Retention
Best for
Free
50
10 MB
24 hours
Testing CADLens
Starter
500
25 MB
7 days
Small apps / prototypes
Growth
2,000
50 MB
14 days
Early production
Pro
10,000
100 MB
30 days
Higher-volume apps
Business
40,000
250 MB
30 days
High volume
Enterprise
Unlimited
Custom
Custom
SLA, private deployment
These mirror GET /v1/pricing, which is the source of truth — the pricing page reads from it live.
Once you hit your plan's limit, POST /v1/parse returns 429 Too Many Requests with a message like Monthly limit of N requests reached for PLAN plan until the next monthly reset or a plan upgrade.
Need more? Upgrade your plan in the dashboard, or email [email protected] for Enterprise.
Quota exceeded
{
"error": "Monthly limit of 50 requests reached for FREE plan"
}
3D solid drawings now render their hole and gland circles. Drawings containing ACIS 3DSOLID geometry previously dropped circular edges whose records carry a common attribute prefix — mounting-plate holes, cable-gland openings, and bolt circles were missing from PNG previews and the viewer, and mis-read arc records could inflate the drawing extents so content appeared tiny in a huge empty canvas. The ACIS record reader now locates geometry robustly regardless of record layout, extracting every solid edge (a reference drawing went from 388 dropped edges to zero). Results carry parserVersion 2.2.1; response shapes are unchanged and 2D drawings are unaffected.
2026-07-17
added
Solid-model drawings now render in 2D. DWG/DXF files containing ACIS 3DSOLID or BODY geometry previously showed nothing but text in model space. CADLens now extracts an edge wireframe directly from the solid's ACIS data — reading both the obfuscated SAT text form and the binary SAB form used by newer conversions — and injects it as ordinary line/polyline geometry, so solids now appear in the PNG preview, the 2D dashboard viewer, and as 3D lines in the 3D viewer. This is additive: it only affects drawings that contain solids, response shapes are unchanged, and 2D-only drawings behave exactly as before. Results carry parserVersion 2.2.0. Note: silhouette/hidden-line curves that some CAD tools regenerate for saved 2D views (e.g. some hole circles) may still be absent — see the 2.2.1 entry above for a related fix to dropped hole circles.
2026-07-16
fixed
Large drawings parse roughly 3× faster. Two internal optimizations for drawings whose converted geometry is very large (hundreds of MB to multi-GB): redundant re-conversion passes that could not change the outcome are skipped, and the streaming geometry filter fast-forwards over out-of-budget sections instead of scanning them line by line. Results are identical — same entities, layers, images, and truncated flag — only faster. Conversion time limits are also configurable server-side now, so very complex drawings that legitimately need several minutes to convert no longer fail early. No API shape changes.
2026-07-15
fixed
Sync mode no longer makes you wait for large files. POST /v1/parse with mode=sync now returns 202 immediately for uploads of 10 MB or more — with a message field explaining that parsing continues asynchronously (poll GET /v1/jobs/:jobId or use a webhook) — instead of holding the connection until the sync timeout. If a smaller drawing turns out to convert to very large geometry mid-parse, the sync wait is also released early with the same 202. Response shapes are unchanged; the 202 body and its optional message field already existed. Tip: prefer mode=async (the default) for large files.
2026-07-15
fixed
Very large drawings now parse instead of failing. Drawings whose converted geometry previously exceeded the processing limit (FILE_TOO_COMPLEX) are now processed through a constant-memory streaming filter: full layer/layout/metadata fidelity is kept, and geometry beyond the entity budget is cleanly dropped with truncated: true set in the result — the same flag already used when a drawing exceeds the 50,000-entity result cap. Only extreme conversions (over 10 GB of intermediate geometry) are still rejected with FILE_TOO_COMPLEX. No API shape changes; drawings that parsed before behave exactly as before.
2026-07-14
fixed
Large-file reliability: drawings whose converted geometry exceeds the processing limits now fail fast with a clear FAILED status and error message instead of remaining stuck in PROCESSING. Failure emails and job.failed webhooks now fire for every terminal outcome, including jobs interrupted by a server restart — an automatic recovery watchdog re-processes or fails any interrupted job, so no job can stay in PROCESSING indefinitely. No API shape changes: statuses, response fields, webhook and email payloads are unchanged.
2026-07-13
schema
Schema v2.0.0 — restructured entity envelope (breaking). Result responses (GET /v1/jobs/:id/result and sync POST /v1/parse) now carry top-level schemaVersion and parserVersion fields, a parseInfo section ({durationMs, warnings, errors} — durationMs is null for jobs parsed before this release), and summary.statistics with byType and byCategory entity counts. Breaking change to sheets[].entities: flat coordinate fields (start, end, vertices, center, position, radius, angles, scales) moved into a geometry object — read coordinates from entity.geometry now. Each entity also gains handle (the original CAD handle, or null — never derived from id), category (Geometry / Annotation / BlockReference / Hatch / Other), and always-present sibling objects: properties (colorIndex, lineType, lineweight, visible, plus HATCH pattern fields), bbox and metrics (computed helpers rounded to 6 decimals, null where not applicable), text (TEXT/MTEXT value/height/style) and reference (INSERT blockName). Original coordinate precision inside geometry is unchanged. Webhook payloads gain an additive result.schemaVersion field only. The viewer/render-entities endpoint is unaffected.
2026-07-12
fixed
Friendlier duplicate-upload protection on POST /v1/parse. Re-uploading a file with the same name is now only rejected when it arrives less than 10 seconds after the previous accepted upload (e.g. a double-click), or when uploads of that filename follow a regular bot-like schedule over the last 24 hours. Each rejection is a 429 with code DUPLICATE_FILE_WARNING and attempt/maxAttempts fields; after 5 violations that filename (only) is blocked for 5 minutes — the response carries code DUPLICATE_FILE_BLOCKED, retryAfterSeconds, and a Retry-After header. Waiting 10 seconds between re-uploads always succeeds, and uploads of other filenames are never affected.
2026-07-07
fixed
Viewer auto-fit and faster loads. Drawings containing stray far-away entities (orphaned block debris millions of units from the real content) no longer open as an invisible speck: the per-sheet bounding box in render data is now outlier-robust — when the raw extent dwarfs the drawing's validated header extent, entities outside 10× the 1st–99th percentile span are excluded from the fit box (all entities are still returned and rendered). Drawings whose only 3D content is unsupported solid types no longer report is3D with an empty 3D scene; a new additive metadata.unsupported3DCount field reports the count. Performance: job list responses no longer carry undocumented multi-MB result payloads (the documented Job shape is unchanged), the dashboard viewer downloads geometry once instead of twice (render data now bundles a meta object with is3D/units/format/filename), immutable result endpoints are HTTP-cached with ETags (reloads hit 304s), and expanded render entities are cached server-side for repeat opens.
2026-07-06
added
Background jobs, email notifications, and lighter webhooks. Large uploads keep processing server-side even if you close the page: dashboard uploads automatically email your account address when the job finishes (only if you left before it completed — live watchers are never emailed), and API callers can opt in by passing an optional notifyEmail form field to POST /v1/parse. The email deep-links to the job detail in your dashboard; if you're signed out you'll be asked to log in and land directly on the job. Webhook payloads are now lightweight: sheets[] carries per-sheet metadata only (name, key, entityCount, layerCount, boundingBox, area, perimeter, imageUrl) plus a new resultUrl field — fetch the full entities/layers geometry from GET /v1/jobs/:id/result, which is unchanged. This fixes webhook delivery timeouts on large drawings (multi-MB payloads previously exhausted retries). Also in this release: status polls may send watch=1 to signal a live viewer, parsing runs in a dedicated worker thread so the API stays responsive during heavy conversions, queued jobs/webhooks/emails process concurrently, and upload timeouts were raised for multi-MB files. API responses are now gzip-compressed when clients send Accept-Encoding (all HTTP clients do, automatically) — large results download ~12× faster with no client changes needed — and the dashboard viewer loads and pans large drawings much more smoothly (idle redraws eliminated, cached geometry, lightweight frames while panning).
2026-07-02
fixed
Viewer accuracy: entities drawn in a mirrored coordinate system (DXF extrusion normal 0,0,−1 — produced by AutoCAD MIRROR) now render at the correct position. Previously mirrored blocks (chairs, sofa cushions, fixtures) appeared far outside the drawing or at flipped positions. Nested block references under mirrored inserts also rotate correctly, and 2D entities with small Z-coordinate artifacts are no longer dropped from results.
2026-07-02
schema
HATCH patternLines: hatch entities in sheets[].entities now include the exact pattern-definition line families (angle, base point, offset, dash lengths — drawing units, rotation/scale applied). Viewers can render true hatch patterns (ANSI31–37, HONEY, STEEL, user patterns) instead of generic diagonals. patternAngle and patternScale are now reliably populated, and metadata.linetypePatterns / metadata.ltscale expose the LTYPE dash tables.
2026-06-30
added
Viewer optimization: the playground viewer resolves BYBLOCK/BYLAYER colors and linetypes through the full DXF inheritance chain, renders dashed/center/hidden linetypes from the drawing's LTYPE table, and ships per-sheet linetypePatterns and ltscale in render data. Entity batching, frustum culling, and level-of-detail keep large drawings interactive.
2026-06-27
added
Interactive viewer: full geometry rendering for complex DWG files. Block references (INSERT entities) are expanded server-side, text labels (TEXT/MTEXT) now appear on canvas, and paper-space layout tabs correctly project model-space content at viewport scale.
2026-06-25
added
sheet.key: each sheet in the sheets[] array now includes a key field — a unique HTML/CSS-safe slug derived from the display label (e.g. "floor-plan-2"). Deduplicated with a -2, -3 suffix when multiple sheets share the same name. Safe to use as an HTML id attribute.
2026-06-25
added
metadata.layoutLabels and metadata.layoutKeys added to result responses. layoutLabels contains the original sheet display names in image order (may repeat across sheets). layoutKeys contains the HTML/CSS-safe slugs, parallel to the existing layouts[] array.
2026-06-25
fixed
Per-viewport frozen-layer rendering: named layout sheets now correctly show only the layers that are visible in each viewport. Previously, all layers rendered on every sheet regardless of viewport freeze settings.
2026-06
added
Multi-script font support in PNG previews: Chinese (Simplified & Traditional), Japanese, Korean, Arabic, Hebrew, Thai, Cyrillic, Devanagari, and Latin text now render correctly in preview images. Previously only Latin characters were supported — non-Latin glyphs appeared as empty boxes. \U+XXXX Unicode escape sequences in DXF MTEXT entities are also decoded, preserving CJK labels embedded in older DXF files.
2026-06
schema
Result response restructured: entities and layers are now nested inside each sheets[] entry instead of being exposed as flat root-level vectorJson / layersJson arrays. Each sheet object contains name, index, imageUrl, entityCount, layerCount, boundingBox, area, perimeter, layers[], and entities[]. New top-level file and summary fields provide file metadata and aggregate counts. The root-level metadata and imageUrl/imageUrls fields are retained for backward compatibility.
2026-06
fixed
Auth loop: when dashboard localStorage was cleared while the frontend still held a valid JWT, the two apps redirected each other in an infinite loop. The login page now re-syncs the token to the dashboard via /auth/callback when the redirect origin is the dashboard.
2026-06
fixed
Sheet mapping: entities were incorrectly lumped into a single Model sheet when the parser omitted the layouts array from metadata. Entity layout tags are now used as the authoritative sheet grouping source.
2026-06
schema
imageUrls array added to result and image responses — one presigned S3 URL per layout sheet. Multi-sheet DWG/DXF/DWF files now produce a separate PNG preview per sheet.
2026-06
fixed
CAD preview rendering: DIMENSION lines and arrows, SOLID arrowheads, and ATTRIB attribute text (title block fields) now appear in rendered preview PNGs.
2026-05
schema
Result documents return typed entities, per-layer counts, drawing metadata with a boundingBox, and a signed PNG preview URL.
2026-05
added
Webhooks: job.processing, job.completed, and job.failed events, delivered to a per-parse webhookUrl or a saved endpoint.
2026-05
added
Format support: DWG, DXF, DWF, DWFx, Bentley DGN V7, and CAD-bearing PDF, validated by magic bytes up to 100 MB.