Time budget: ~2 hours. We respect your time — please don't spend significantly longer. An incomplete solution with clear reasoning beats a polished one that took a whole weekend.
Our platform is built on Restate, a framework for building resilient, durable services. We work in the manufacturing domain, where a common task is integrating ERP systems with shop-floor systems using the ISA-95 standard as a common language.
In this exercise you'll extend Restate's hello-world example into a small integration pipeline: receive a production order from an (imaginary) ERP, transform it into a simplified ISA-95 structure, and write the result to a file.
You may use any language with a Restate SDK (TypeScript, Python, Java, Kotlin, Go, Rust...) — use whichever you're most comfortable with.
⚠️ There are no provided restate binaries for Windows: We recommend using NPM/Node in this case
Follow the Restate Quickstart for your chosen language:
- Install the Restate Server & CLI.
- Create the Greeter template project (e.g.
npx -y @restatedev/create-app@latestfor TypeScript — the quickstart shows equivalents for other languages). - Run the service, register it with the Restate server, and confirm the Greeter responds:
curl localhost:8080/restate/call/Greeter/greet --json '{"name": "Sarah"}'Keep the Greeter in your project — it's a handy reference for handler syntax.
Add a new Restate service OrderIngestion with a handler submitOrder that:
- Accepts an ERP production order (JSON payload below).
- Validates it: required fields present,
quantity > 0,plannedEndafterplannedStart. Reject invalid orders with a meaningful error. - Calls the
Isa95Mapperservice (Part 2) to transform and persist the order. - Returns a confirmation containing the order number and the path of the file that was written.
📝 Check the Restate Docs for your chosen SDK on how to call another service
This payload (and four more, including one deliberately invalid order for testing your validation) is provided in the sample-orders/ folder.
{
"orderNumber": "PO-2026-004512",
"erpSystem": "SAP-PP",
"material": {
"materialNumber": "FG-8842",
"description": "Stainless Steel Valve Assembly DN50",
"uom": "EA"
},
"quantity": 250,
"plannedStart": "2026-08-03T06:00:00Z",
"plannedEnd": "2026-08-05T18:00:00Z",
"priority": 2,
"plant": "DE01",
"routing": [
{ "operation": "0010", "workCenter": "CNC-LATHE-3", "description": "Turn valve body", "setupMinutes": 45, "runMinutesPerUnit": 4.5 },
{ "operation": "0020", "workCenter": "ASSY-LINE-1", "description": "Assemble valve", "setupMinutes": 20, "runMinutesPerUnit": 6.0 },
{ "operation": "0030", "workCenter": "QA-BENCH-2", "description": "Pressure test", "setupMinutes": 10, "runMinutesPerUnit": 2.0 }
],
"components": [
{ "materialNumber": "RM-1001", "description": "Steel bar stock 60mm", "quantityPer": 1.2, "uom": "KG" },
{ "materialNumber": "PM-2205", "description": "Seal kit DN50", "quantityPer": 1.0, "uom": "EA" }
]
}Add a second service Isa95Mapper with a handler mapAndStore that:
- Transforms the ERP order into the simplified ISA-95 Operations Request structure below.
- Writes it as pretty-printed JSON to
./output/<orderNumber>.json. - Returns the file path.
This is the complete expected output for the sample payload above — your output/PO-2026-004512.json should match it exactly:
{
"operationsRequest": {
"id": "PO-2026-004512",
"operationsType": "Production",
"startTime": "2026-08-03T06:00:00Z",
"endTime": "2026-08-05T18:00:00Z",
"priority": 2,
"hierarchyScope": { "id": "DE01", "equipmentLevel": "Site" },
"segmentRequirements": [
{
"id": "PO-2026-004512-0010",
"description": "Turn valve body",
"equipmentRequirement": { "id": "CNC-LATHE-3" },
"duration": 1170,
"materialRequirements": [
{
"id": "RM-1001",
"materialUse": "Consumed",
"quantity": { "value": 300, "unitOfMeasure": "KG" }
},
{
"id": "PM-2205",
"materialUse": "Consumed",
"quantity": { "value": 250, "unitOfMeasure": "EA" }
}
]
},
{
"id": "PO-2026-004512-0020",
"description": "Assemble valve",
"equipmentRequirement": { "id": "ASSY-LINE-1" },
"duration": 1520,
"materialRequirements": []
},
{
"id": "PO-2026-004512-0030",
"description": "Pressure test",
"equipmentRequirement": { "id": "QA-BENCH-2" },
"duration": 510,
"materialRequirements": [
{
"id": "FG-8842",
"materialUse": "Produced",
"quantity": { "value": 250, "unitOfMeasure": "EA" }
}
]
}
]
}
}
A visual reference of the full mapping (source model, target model, and numbered mapping rules) is provided in mapping-diagram.png (editable source: mapping-diagram.drawio).
Mapping rules:
- One
segmentRequirementper routing operation;id=<orderNumber>-<operation>. duration= setup time + (run time per unit × order quantity), given in minutes.- Attach each component as a
materialRequirementon the first segment only, withquantity.value=quantityPer × order quantityandmaterialUse: "Consumed". - The produced material goes on the last segment as a
materialRequirementwithmaterialUse: "Produced"and the full order quantity.
Restate hint: side effects like file writes should be wrapped appropriately so they behave correctly under Restate's durable execution model. Look at ctx.run (or your SDK's equivalent) and be ready to explain why it matters.
Only if you have time left within the 2 hours — this is genuinely optional and also a discussion topic in the follow-up interview.
Each order's routing references work centers (e.g. CNC-LATHE-3). Using a Restate Virtual Object keyed by work center ID, implement (or sketch in your README) a WorkCenter object that:
- Tracks the total scheduled minutes booked against that work center.
- Exposes a handler to query its current load.
- Is updated by the pipeline whenever an order is processed.
Consider: what does keying by work center ID give you when two orders touching the same work center arrive concurrently?
- A git repository (zip or link) containing your code, the generated
output/sample file for the payload above, and a README with: how to run it, your Part 3 answers, any assumptions or shortcuts, and what you'd do next with more time. - Please include your commit history — we like seeing how you work incrementally.
- It works: the sample payload flows through and produces a correct ISA-95 file.
- Restate understanding: sensible use of services, service-to-service calls, and durable side effects. We don't expect you to fully understand the internals of Restate, but knowledge of the basics will help.
- Code quality: clear naming, small functions, sensible validation and error handling. No over-engineering.
- Communication: a README that explains your thinking.
We do not expect: tests beyond one or two happy-path checks, deployment setup, exhaustive ISA-95 coverage, or a UI.
Questions or blockers? Email us — asking a good question counts in your favour.