Sheet Lifecycle: Create, Update & Re-solve
Iterate on one design's sheet instead of creating a new sheet per run. Create an unsolved sheet for any template, set or change its inputs, and solve it as many times as it takes to reach a passing design — the sheetId stays the same across every pass.
This guide walks the same snow load calculation as the Quick Example: there, POST /v1/templates/{code}/calculate answers it in one call; here, the same inputs reach the same results step by step. Use calculate when one set of inputs gives you your answer; use the lifecycle when you expect to refine and re-solve.
Endpoints
Code
Create Sheet → · Get Sheet → · Update Sheet Inputs → · Solve Sheet →
1. Create an unsolved sheet
Create a sheet from any template code (discover codes via GET /v1/templates). The sheet starts unsolved — no calculation runs until you solve it:
Code
Code
name is optional — it defaults to "<template name> - API". sheetUrl opens the sheet in the Calcs.com app.
2. Update its inputs
Attribute keys are widget reference IDs from the template — inspect them with GET /v1/templates/{code}. A key that matches no widget on the template is skipped: it is listed in warnings and left out of updatedAttributes, so compare the two to confirm a write landed.
These are the same four attributes the Quick Example sends to calculate. For a field whose type is enum, send one of the labels listed in that field's options:
Code
Code
Updating does not recalculate — solve the sheet to refresh results.
The attributes are applied as one unit: either the whole set is written or, if any value is rejected, none of it is. A sheet never holds part of a PATCH.
updatedAttributes is the write's receipt — exactly the keys this request stored. A mistyped key is absent from it and named in warnings:
Code
warnings is absent when there is nothing to report.
An explicit null is not a clear: the key is skipped, left out of updatedAttributes, and reported with a null_value warning. To leave a field alone, omit it.
3. Solve it
Code
Code
Solving can take a few seconds. A failing engineering check is still a 200 — the calculation ran, so read the check to see how it came out. results keys depend on the template's configured API outputs, and for the same template and inputs they match what calculate returns.
Solving is safe to retry. If a call times out or drops mid-flight, re-issue it: a solve runs the sheet against the inputs it holds, so repeating it with unchanged inputs returns the same results and never creates another sheet.
A solve response can come back unsolved. If another PATCH lands on the sheet while the solve is running, the results no longer describe the sheet's current inputs, and the response reports status: "unsolved" rather than attributing them to inputs they weren't computed from. Solve again to get results for the new inputs — or serialise writes and solves per sheet, which avoids the race entirely.
Reading the verdict
Use summaryCheck for the outcome of the solve you just ran. It is the value of the template's governing check, alongside summaryCheckReferenceId (which widget it is), summaryCheckError, and summaryCheckFailReasons when the check failed. The check passes when summaryCheckError is false and summaryCheck is either true or a number from 0 to 1 — the same rule the Calcs.com app applies. A template with no governing check returns no summaryCheck keys and so has no verdict to report.
passed answers a different question: whether the sheet has ever passed. It becomes true the first time a sheet passes and stays true afterwards, so it is null on a sheet that has only ever failed and can still be true on a sheet that has since been edited into failing. Do not read it as the result of the latest solve.
To know whether this design, as it stands now, passes, read two fields together: status must be solved — the results were computed from the inputs the sheet currently holds — and the summaryCheck fields must report a pass. status alone says the results are current; summaryCheck alone could be reporting a superseded solve; the pair is the verdict.
4. Read it back any time
Code
Code
status tells you whether the results belong to the inputs. It is solved only while the stored results were computed from the inputs the sheet holds now. A PATCH moves the sheet back to unsolved, and while it is unsolved the response carries no results, passed, or check keys — re-solve to get them for the current inputs.
inputs reports stored values, which are not always the values you sent. Numbers read back as strings ("30") and booleans as "1" or "0".
For an enum field, treat the stored value as opaque. Writes take the label — one of the strings in that field's options from GET /v1/templates/{code} — but what comes back is whatever the template stores internally, and that varies field by field. In the read above, exposure reads back as its label while cond_thermal reads back as "1". Both were written as labels.
Do not reconstruct an enum's label from the value you read back. Nothing in the response tells you which representation a field uses, and one of them is a number that looks like a position in options but is not — indexing options with it returns a different option, with no error. If you need to display or re-send an enum, keep the label you wrote rather than deriving it from the read.
Do not depend on the stored form itself either: a future release may return the option label here instead.
A numeric value you send for an enum is stored as-is and is not range-checked against the field's options.
Iterating: converge on a passing design
Change an input, re-solve, and check the verdict — same sheetId every pass. Here the roof width W is refined on the sheet created in step 1 (requires jq):
Code
Each pass refines the same design — one sheet, carried from first guess to the configuration you keep. When the loop ends, that sheet holds the passing design. (Deleting sheets you no longer need is covered in Deleting & Cleanup.)
Errors
If a sheet id does not exist or is not accessible with this API key, you get 404 Not Found:
Code
A request body that is well-formed JSON but invalid — an unrecognized field, or attributes missing, empty, or not an object — is rejected with 422 Unprocessable Entity before anything is changed (this pre-flight check covers the request shape; a value the template itself rejects — a non-numeric width, say — also returns 422, naming the field in details. The sheet is left unchanged either way):
Code
Malformed JSON is a 400. A solve that completes without producing results — usually missing required inputs, or a template with no API outputs configured — is a 422 with code CALCULATION_ERROR.
Every error on these routes carries one of three error.code values — branch on the code, not the message:
error.code | Status | Means |
|---|---|---|
NOT_FOUND | 404 | The sheet, template, or project does not exist, or is not accessible with this API key |
VALIDATION_ERROR | 400 / 422 | 400 for malformed JSON or a non-UUID id; 422 for a well-formed request the template or endpoint rejects — nothing is written |
CALCULATION_ERROR | 422 | The solve ran but produced no results |
A 200 on PATCH can still carry warnings, each with a type of unknown_field (a key matching no widget — skipped) or null_value (an explicit null — skipped, not a clear). A warned key is always absent from updatedAttributes.