About two minutes. To keep files on your machine, run the same operation locally.
1. Get an API key
Create an account or sign in. Open Setup → API keys in the console and choose Create API key. A new account gets a free trial of 100 successful output files for 30 days, with no card. A run that returns several files counts once per file. Then Platform or Enterprise continues. Keep the key in an environment variable:
export RUNSTAMP_API_KEY=pj_live_YOUR_KEY2. Send a sample document
Download the sample workbook. Ask Runstamp to write "Ready for review" into cell E2 of its Remediation sheet.
SAMPLES=https://runstamp.com/demo-evidence/operation-evidence
curl -fsSL -o sample.xlsx \
"$SAMPLES/xlsx/xlsx.transform/input-01-workbookBase64.xlsx"
RUNSTAMP=https://runstamp.com/api/v2
curl "$RUNSTAMP/operations/xlsx/transform" \
-H "Authorization: Bearer $RUNSTAMP_API_KEY" \
-H "Idempotency-Key: quick-start-$(date +%s)" \
-H "Runstamp-Document-Name: sample.xlsx" \
-H "Content-Type: application/json" \
-d @- <<JSON
{
"input": {
"workbookBase64": "$(base64 < sample.xlsx | tr -d '\n')",
"artifactId": "quick-start-workbook",
"writes": [{
"locator": {
"artifactId": "quick-start-workbook",
"scheme": "xlsx.a1",
"value": ["Remediation", "E2"]
},
"value": "Ready for review"
}]
}
}
JSON// Save as quick-start-managed.mjs and run: node quick-start-managed.mjs
import { randomUUID } from "node:crypto";
const SAMPLES = "https://runstamp.com/demo-evidence/operation-evidence";
const RUNSTAMP = "https://runstamp.com/api/v2";
const workbook = Buffer.from(await fetch(
`${SAMPLES}/xlsx/xlsx.transform/input-01-workbookBase64.xlsx`,
).then((response) => response.arrayBuffer()));
const response = await fetch(`${RUNSTAMP}/operations/xlsx/transform`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RUNSTAMP_API_KEY}`,
"Idempotency-Key": randomUUID(),
"Runstamp-Document-Name": "sample.xlsx",
"Content-Type": "application/json",
},
body: JSON.stringify({
input: {
workbookBase64: workbook.toString("base64"),
artifactId: "quick-start-workbook",
writes: [{
locator: {
artifactId: "quick-start-workbook",
scheme: "xlsx.a1",
value: ["Remediation", "E2"],
},
value: "Ready for review",
}],
},
}),
});
const run = await response.json();
if (!response.ok) {
console.error(`Runstamp returned ${response.status}: ${run.error ?? "request failed"} (${run.code ?? "no code"}). Run it again if the status is 503.`);
process.exit(1);
}
console.log(run.id, run.status_url);Runstamp returns 202 Accepted and the run ID:
{
"id": "4f6c2a8e-9b1d-4c3e-8a57-2d0f1e6b9c41",
"status": "queued",
"status_url": "/api/v2/executions/4f6c2a8e-9b1d-4c3e-8a57-2d0f1e6b9c41",
"result_url": "/api/v2/executions/4f6c2a8e-9b1d-4c3e-8a57-2d0f1e6b9c41/result",
"reused": false
}3. See the run
Open https://runstamp.com/dashboard/runs/RUN_ID to see the changed cell, the checks run and the file. From code, poll the status URL, then download the result:
RUNSTAMP=https://runstamp.com/api/v2
curl "$RUNSTAMP/executions/RUN_ID" \
-H "Authorization: Bearer $RUNSTAMP_API_KEY"
curl -L "$RUNSTAMP/executions/RUN_ID/result" \
-H "Authorization: Bearer $RUNSTAMP_API_KEY" \
-o result.jsonA large result returns a 307 redirect, so keep -L. For files over 4 MiB, see Large inputs and results.
The result is JSON. The changed workbook is inside it as base64 at value.output.bytes.data, beside the receipt and the list of changes. Once the status is succeeded and the result has "ok": true, save the file with jq:
jq -r '.value.output.bytes.data' result.json | base64 --decode > output.xlsxUse the Runstamp Managed API to set cell E2 on the "Remediation" sheet of the sample workbook below to "Ready for review", wait for the run to finish, and print the run ID.
Sample workbook: https://runstamp.com/demo-evidence/operation-evidence/xlsx/xlsx.transform/input-01-workbookBase64.xlsx
POST the base64 workbook to https://runstamp.com/api/v2/operations/xlsx/transform with Authorization: Bearer $RUNSTAMP_API_KEY and an Idempotency-Key header, then poll the returned status_url until the run finishes. Docs for agents: https://runstamp.com/llms.txt
Run it locally with the SDK
The same operation runs in your process with the SDK. No account or API key is needed, and the workbook stays on your machine. It needs Node.js 20 or later.
1. Install and initialize
In an empty folder, create a project and install the SDK:
mkdir runstamp-quickstart && cd runstamp-quickstart
npm init -y
npm install @runstamp/sdkCreate a file named quickstart.mjs in that folder. Paste this at the top, then add the code from steps 2, 3 and 4 below it, in order, in the same file. The .mjs extension tells Node to treat the file as an ES module, so import and top-level await work on every Node version the SDK supports, with no other setup.
import { writeFile } from "node:fs/promises";
import { runstamp } from "@runstamp/sdk";
const SAMPLES = "https://runstamp.com/demo-evidence/operation-evidence";
const source = Buffer.from(await fetch(
`${SAMPLES}/xlsx/xlsx.transform/input-01-workbookBase64.xlsx`,
).then((response) => response.arrayBuffer()));
const artifactId = "security-remediation-workbook";The local transport runs the stable catalog in your process. The workbook is not sent to hosted execution.
2. Find the cell to change
Map the cell your app will change. In production, use a defined name or native table if the customer template has one. This sample uses a known A1 cell.
const mapped = await runstamp.operations.run("xlsx.transform", {
workbookBase64: source.toString("base64"),
artifactId,
targets: [{
id: "remediation-status",
kind: "a1",
sheet: "Remediation",
ref: "E2",
}],
});
if (!mapped.ok) throw new Error(`${mapped.error.code}: ${mapped.error.remediation}`);
const target = mapped.value.output.mappings[0]?.locator;
if (!target) throw new Error("The requested target was not resolved.");
const before = await runstamp.operations.run("xlsx.parse", {
workbookBase64: source.toString("base64"),
artifactId,
locators: [target],
});
if (!before.ok) throw new Error(`${before.error.code}: ${before.error.remediation}`);
console.log("Current value:", before.value.output.cells[0]?.value);Before writing, show the resolved target and current value to the owner of the change.
3. Change and verify the existing file
const changed = await runstamp.operations.run("xlsx.transform", {
workbookBase64: source.toString("base64"),
artifactId,
writes: [{
locator: target,
value: "Ready for review",
}],
});
if (!changed.ok) {
throw new Error(`${changed.error.code}: ${changed.error.remediation}`);
}
const ladder = changed.verification?.ladder;
if (ladder?.valid.state !== "pass"
|| ladder.preserved.state !== "pass"
|| ladder.intendedChangeVerified.state !== "pass") {
throw new Error("The changed workbook did not pass the required mutation checks.");
}
await writeFile("remediation-reviewed.xlsx", changed.value.output.bytes);
console.log("Wrote remediation-reviewed.xlsx");
for (const change of changed.value.mutation.changes) {
const [sheet, cell] = change.locator.value;
const cause = change.cause ? ` (${change.cause})` : "";
console.log(`Changed ${sheet}!${cell}: ${JSON.stringify(change.before.value)} -> ${JSON.stringify(change.after.value)}${cause}`);
}changed.value.mutation.changes is the change record: every cell whose stored value changed, with its value before and after. A formula cell that recalculated because of your write is listed too, marked (recalculation).
The operation reopens the output, checks its package structure, confirms the declared value exists, and checks that content outside the target did not change. A no-op or unobserved change returns a typed failure.
4. Know what was not checked
console.log(changed.verification);
// valid: pass
// preserved: pass
// intendedChangeVerified: pass
// deliveryReady: not_checked
// businessCorrectness: not_verifiableThis check does not show that Excel renders the workbook correctly in your recipient's apps and versions. It does not prove the status fits the business. Run those checks before delivery or require them in a managed acceptance profile.
5. Run it
node quickstart.mjsIt prints the current value of the cell (Current value: In Progress), writes remediation-reviewed.xlsx next to the script, prints the change record (Changed Remediation!E2: "In Progress" -> "Ready for review"), then the verification result with valid, preserved and intendedChangeVerified at pass. Open the file to see "Ready for review" in cell E2 of the Remediation sheet.
Use the same operation elsewhere
The operation name, schemas, result envelope, errors, losses, receipt and verification evidence keep the same shape across the local SDK, CLI, MCP and Managed API.