Skip to content

Embedded Diagram

Embed an interactive cutting layout visualisation on your website using a simple iframe. It displays the optimised cut layout for a given calculation result. It can also talk to the surrounding page via postMessage.

Point it at a result of ours by job ID (result), or at a copy of the result JSON you host yourself (src, see Your own JSON).

<!-- Replace the result value with your calculation job ID -->
<div id="smartcut-vis-message">Loading...</div>
<iframe
id="smartcut-vis"
src="https://cutlistevo.com/embed/?result=1733"
allowtransparency="true"
frameborder="0"
height="0"
></iframe>

Set the src attribute dynamically using the jobId returned by the /v3/calculate API call:

const jobId = 1733 // returned by /v3/calculate
document.getElementById('smartcut-vis').src =
`https://cutlistevo.com/embed/?result=${jobId}`

A live example is available at: https://cutlistevo.com/embed/?result=1733

A result stays available for 24 hours. Past that the diagram shows “no result”, unless you kept the JSON and served it yourself, which is what src is for.

Pass parameters as query string values on the iframe src URL.

Parameter Description
result The calculation job ID to display. Required unless src is given
src URL of a result JSON you host yourself, described in Your own JSON. Used in place of result
dn Set to true to disable the stock navigation controls
hs Set to true to hide the stock information header

Save the result JSON when you receive it, put it anywhere you can serve a file from, whether that is S3, your own CDN or your app’s storage, then point the embed at that URL with src instead of result:

const src = encodeURIComponent('https://cdn.example.com/jobs/1733.json')
document.getElementById('smartcut-vis').src =
`https://cutlistevo.com/embed/?src=${src}`

URL-encode the value, so a query string of its own does not merge into the embed’s. Every other parameter behaves exactly as it does for result, including colours, navigation and postMessage.

This is worth doing when:

  • the diagram has to outlive our copy, such as an order confirmation, an archived quote or a job sheet a customer opens weeks later
  • the page must not depend on our API. With src the iframe never calls us, so the layout still draws if our service is unreachable
  • the result never came from a job you can re-request, because you hold the JSON and not a job ID we can look up

Either of the two result documents you can already obtain:

Document Where it comes from
V3 API result the body of GET /v3/result?id=…, or the result delivered for POST /v3/calculate
Embed result the body of GET /result/embedded?id=…

V1/V2 API results are not accepted. They name sides x1/x2/y1/y2 and nest parts under each stock, which is a different document rather than a renamed one. Re-fetch the job through V3, or store the embed result.

Store the response body verbatim, with no re-shaping and no unwrapping. If your own records wrap it ({ order, result }), keep the result at a URL of its own:

// when the calculation completes, keep a copy
const result = await fetch(`https://api.smartcut.dev/v3/result?id=${jobId}`, {
headers: { Authorization: process.env.SMARTCUT_API_KEY } // raw key, no Bearer
}).then((r) => r.json())
await putSomewherePublic(`jobs/${jobId}.json`, JSON.stringify(result))
  • Serve the file over HTTPS from an absolute URL. An http URL is blocked as mixed content by the browser.
  • Send Access-Control-Allow-Origin: https://cutlistevo.com (or *) with the file. The iframe fetches it directly, so without CORS the browser blocks it.
  • Keep the file publicly readable: the request is sent without credentials, so cookies and auth headers of yours play no part.
  • Stay under 25 MB.

The reason is posted to the surrounding page as an error message (see iframe Messages) and logged to the iframe’s console:

Message Meaning
src must be an absolute URL the value was relative, or not a URL
src must be an http or https URL the value used another scheme (data:, file:, …)
could not fetch the JSON at src the host did not answer, or CORS blocked the read
src responded 404 the host answered, with that status
src did not return valid JSON the body did not parse, often an error page
src JSON must be a V3 API result … the JSON parsed but is not a result document

Customise the colour scheme using HEX values (without the # prefix).

Parameter Description
pca Part colour A
pcb Part colour B
pch Part colour on hover
pcs Part colour when selected
sc Stock (board) background colour
tc Text colour

Example:

https://cutlistevo.com/embed/?result=1733&pca=BAD0F5&pcb=346AC9&pch=E09318&pcs=D35A5A&sc=EBEB58

Communicate with the embed using the browser’s postMessage API.

Listen for these events on window:

Type Description
resize The embed has resized. w and h carry the new dimensions in pixels.
loaded The visualisation has loaded and a result was found. payload carries the stock IDs.
noResult No result was found for the given job ID, or the JSON at src held no placed parts.
error An error occurred. message carries the error text.
partClick A part was clicked. message carries part data.

Send these events to the iframe using contentWindow.postMessage:

Type Fields Description
navigate stockID Navigate to a specific stock item by its ID (e.g. '1.0')
  • The embed must be hosted on a page served over HTTPS. It will not work on HTTP.
  • The iframe height starts at 0. The resize message sets it.
  • A result is kept for 24 hours. Beyond that, render it from your own JSON.
  • src replaces result: give one or the other, and src wins if both are set.
<div id="smartcut-vis-message">Loading...</div>
<iframe
id="smartcut-vis"
src="https://cutlistevo.com/embed/?result=1733"
allowtransparency="true"
frameborder="0"
height="0"
></iframe>

The same iframe, reading JSON you host. Only the query parameter changes:

<iframe
id="smartcut-vis"
src="https://cutlistevo.com/embed/?src=https%3A%2F%2Fcdn.example.com%2Fjobs%2F1733.json"
allowtransparency="true"
frameborder="0"
height="0"
></iframe>
window.addEventListener('message', (e) => {
if (!e.data) return
if (e.data?.origin !== 'smartcut') return
switch (e.data.type) {
case 'resize':
document.getElementById('smartcut-vis').style.height = e.data.h + 'px'
break
case 'loaded':
document.getElementById('smartcut-vis-message').style.display = 'none'
document.getElementById('smartcut-vis').style.visibility = 'visible'
break
case 'noResult':
document.getElementById('smartcut-vis-message').innerText = 'No result found'
break
case 'error':
document.getElementById('smartcut-vis-message').innerText = e.data.message
break
case 'partClick':
console.log(e.data.message)
break
}
}, false)
// Navigate to a specific stock item
document.getElementById('smartcut-vis').contentWindow.postMessage(
{ type: 'navigate', stockID: '1.0' }
)
#smartcut-vis,
#smartcut-vis-message {
width: 100%;
max-width: 1000px;
}
#smartcut-vis {
background-color: rgba(255, 255, 255, 0.3);
box-sizing: border-box;
visibility: hidden;
}