*How We Built the Backend for WorkMemory AI
*
An engineering incident may be solved in a few hours, but the useful knowledge from that incident can easily disappear afterward.
While working on WorkMemory AI, I focused on the backend that connects the incident dashboard with the memory and investigation workflow. The goal was simple: when an engineer reports a problem, the backend should provide a structured way to store that incident and prepare it for future investigation and organizational memory.
What Is WorkMemory AI?
WorkMemory AI is an incident-response assistant designed around a simple idea:
«An engineer should not always have to start from zero when a similar incident has happened before.»
In a software team, information about incidents can be spread across tickets, documentation, chats, troubleshooting notes, and deployment discussions.
For example, imagine that a Payment API starts returning HTTP 500 errors after a deployment.
An engineer might eventually discover that an environment configuration was incorrect. Months later, a similar problem occurs.
Without an organized memory system, the second investigation may begin almost from scratch.
WorkMemory AI is designed to connect these two situations by treating previous engineering incidents as useful organizational knowledge.
Figure 1: WorkMemory AI dashboard for recording and investigating engineering incidents.
My Role: Backend
My main focus was the backend.
The backend is built using Node.js and Express.js. It provides API endpoints that the frontend can use to submit incidents and request an investigation.
The basic architecture is:
React + Vite Frontend
|
v
Node.js + Express Backend
|
+---- Incident API
|
+---- Investigation API
|
v
Memory / AI Integration Layer
I wanted the frontend to remain independent from the internal implementation of the memory layer.
The React application only needs to communicate with the backend API. Provider-specific logic can stay inside backend services.
Designing the Incident API
The first important endpoint is:
POST /api/incidents
It receives an incident from the frontend.
The backend first checks whether incident data was actually provided:
router.post("/incidents", async (req, res) => {
try {
const incident = req.body;
if (!incident || Object.keys(incident).length === 0) {
return res.status(400).json({
error: "Incident data is required"
});
}
const memory = await saveToHindsight(incident);
return res.status(201).json({
message: "Incident stored",
memory
});
} catch (error) {
console.error("Error storing incident:", error);
return res.status(500).json({
error: "Failed to store incident",
details: error.message
});
}
});
This structure gives the API a clear responsibility:
- Receive the incident.
- Validate the request.
- Send the incident to the memory service.
- Return a response to the frontend.
- Handle errors without crashing the server.
Figure 2: Testing the incident API from the backend.
Keeping Memory Logic Separate
One of the backend design decisions I found useful was keeping the memory-provider logic separate from the API routes.
Instead of putting every external API request directly inside "incidentRoutes.js", the route calls a service:
const {
saveToHindsight,
investigateWithMemory
} = require("../services/hindsightService");
The route therefore doesn't need to know how the memory system is implemented.
The service acts as an adapter between the application and the memory layer.
In the current project version, this adapter is implemented as a test layer so that the rest of the application can be developed and tested independently.
For example:
async function saveToHindsight(incident) {
console.log("\n[HINDSIGHT] Incident received:");
console.log(JSON.stringify(incident, null, 2));
return {
status: "stored",
source: "temporary-hindsight-adapter",
incident
};
}
This was useful during development because I could test the complete API workflow before connecting the production memory implementation.
The Investigation Endpoint
The second important backend endpoint is:
POST /api/incidents/investigate
Its purpose is different from incident creation.
Instead of simply storing an incident, it represents the investigation stage.
The route validates the request and calls the investigation service:
router.post("/incidents/investigate", async (req, res) => {
try {
const incident = req.body;
if (!incident || Object.keys(incident).length === 0) {
return res.status(400).json({
error: "Incident data is required"
});
}
const result = await **investigateWithMemory(incident);**
return res.status(200).json(result);
} catch (error) {
console.error("Error investigating incident:", error);
return res.status(500).json({
error: "Failed to investigate incident",
details: error.message
});
}
});
The current investigation adapter returns a structured response:
async function investigateWithMemory(incident) {
console.log("\n[HINDSIGHT] Investigation requested:");
console.log(JSON.stringify(incident, null, 2));
return {
message: "Investigation endpoint is working",
incident,
similarIncidents: [],
analysis: {
status: "pending",
message: "Connect Hindsight Recall and the LLM layer next."
}
};
}
This also made the development process easier because the frontend could be connected to the investigation endpoint before the complete memory and LLM layers were finished.
Figure 4: Investigation endpoint returning a structured backend response.
Why Use an Adapter?
The adapter pattern became important for this project.
A frontend should not have to know:
- where the memory service is hosted,
- how authentication works,
- how memory requests are formatted,
- how recall is performed,
- or what happens when the external service fails.
Those responsibilities belong in the backend.
The intended production flow is:
New Incident
|
v
Backend API
|
v
Memory Layer
|
v
Recall Relevant Past Incidents
|
v
LLM Investigation
|
v
Investigation Result
|
v
Frontend
This separation means that the application can evolve without rewriting the frontend whenever the memory implementation changes.
A Simple Example
Consider this incident:
Title:
Payment API 500 Error
Service:
Payment API
Environment:
Production
Description:
Payment API started returning 500 errors after deployment.
Severity:
High
The frontend sends this information to:
POST /api/incidents
The backend validates the request and passes the incident to the memory service.
Later, an engineer could submit a similar incident to:
POST /api/incidents/investigate
The intended memory workflow would then look for relevant previous engineering experiences.
For example, a previous incident might have revealed that an incorrect deployment environment variable caused a similar Payment API failure.
The important idea is not that the previous incident automatically proves the current root cause.
Instead, it gives the engineer a useful starting point:
«“A similar problem happened before. Here is what the team learned then.”»
The engineer can then verify that information against the current system.
Backend Error Handling
Another area I focused on was basic error handling.
For example, if an empty request reaches the API, the backend returns:
return res.status(400).json({
error: "Incident data is required"
});
If an unexpected problem occurs while processing the request:
return res.status(500).json({
error: "Failed to store incident",
details: error.message
});
This makes API failures easier to understand during development and testing.
The server also uses environment variables through "dotenv":
require("dotenv").config();
This allows configuration such as the server port and future external-service credentials to remain outside the source code.
The Backend Stack
The backend uses:
- Node.js
- Express.js
- CORS
- dotenv
- REST API endpoints
- A separate memory-service adapter
- React/Vite frontend integration
The project is developed using Git, GitHub, and VS Code.
Figure 5: Running the WorkMemory AI backend locally.

What I Learned
- Separate application logic from external services
Putting provider-specific code inside a separate service makes the main API routes easier to understand and maintain.
- Build the API boundary first
Having clear endpoints such as "/api/incidents" and "/api/incidents/investigate" made it easier to connect the frontend and develop the rest of the system incrementally.
- Memory and storage solve different problems
A normal incident record answers:
«“What incident did we record?”»
A memory system is intended to answer:
«“Have we experienced something relevant before?”»
That distinction is one of the main ideas behind WorkMemory AI.
4. Build incrementally
We did not need every part of the system to be complete before testing the backend.
The API, frontend, and memory adapter could be tested separately and then connected progressively.
5. AI should support engineering judgment
Even when the memory and LLM layers are connected, their output should be treated as investigation context rather than unquestionable truth.
A previous incident can be similar without having exactly the same root cause.
What Comes Next
The next stage for the backend is completing the real Hindsight integration so that incidents can be retained as persistent engineering memories and relevant memories can be recalled during investigation.
The investigation layer can then pass recalled context to an LLM to generate a structured explanation and recommended next steps.
The intended final workflow is:
Engineer reports incident
↓
Backend receives incident
↓
Incident is retained as organizational knowledge
↓
A similar incident appears later
↓
Backend recalls relevant past experience
↓
LLM analyzes the recalled context
↓
Engineer receives investigation context
↓
New learning can become future memory
This creates a continuous learning loop instead of treating every incident as an isolated event.
Conclusion
The most interesting part of building WorkMemory AI was realizing that an incident should not simply end when the immediate problem is fixed.
The real value can continue afterward if the useful experience is captured and made available when a similar problem appears.
My contribution to that idea was building the backend boundary that connects the frontend, incident workflow, and memory layer.
The current backend gives us a clean foundation for that workflow, while the memory and LLM layers can be extended behind the service boundary.
The idea we want to carry forward is simple:
«Solve the incident once. Preserve what was learned. Make the next investigation start with that knowledge.»
GitHub:
https://github.com/sahasra09k-spec/WorkMemory-AI
Demo:https://photos.app.goo.gl/h1fkWCBvU2xqh2dD9
Hindsight: https://github.com/vectorize-io/hindsight
Hindsight Documentation: https://hindsight.vectorize.io/



Top comments (0)