@linkorb/n8n-nodes-nebula
v1.0.0
Published
n8n nodes for Nebula - Human-in-the-Loop (HITL) requests with configurable REST API backend
Maintainers
Readme
n8n-nodes-nebula
A custom n8n node for implementing Nebula HITL (Human-in-the-Loop) workflows with your own configurable REST API backend.

Description
This node allows you to create human approval/input requests in your n8n workflows. Unlike other HITL solutions that are tied to specific cloud services, this node is designed to work with your own backend, giving you full control over how requests are created, stored, displayed, and responded to.
Features
- ⏸️ True Wait Functionality: Workflow execution pauses until a human responds
- 🔐 Configurable Backend: Connect to your own REST API endpoint
- 📝 Multiple Response Types: Ok, Yes/No, Text, or Custom options
- 🏷️ Rich Metadata: Support for priority, assignee, tags, and custom data
- 🔄 Webhook-based Responses: Automatically resume workflow when human responds
- ⏰ Configurable Timeout: Set how long to wait for a response
- 📊 Full Context: Pass workflow and execution context to your backend
How It Works
- Request Creation: The node POSTs a request to your backend with all details including a webhook URL
- Workflow Pauses: The n8n execution enters a "waiting" state - no resources are consumed while waiting
- Human Action: Your backend displays the request to a human for action
- Webhook Callback: When the human responds, your backend POSTs to the webhook URL
- Workflow Resumes: n8n receives the webhook, resumes the workflow with the response data as output
Installation
Prerequisites
- n8n version 1.0.0 or later
- Node.js 18.10 or later
- pnpm (recommended) or npm
Install via npm (Community Nodes)
In your n8n instance, go to Settings → Community Nodes and install:
n8n-nodes-nebulaManual Installation (Self-hosted n8n)
- Clone or download this repository:
cd ~/.n8n/custom
git clone https://github.com/linkorb/n8n-nodes-nebula.git
cd n8n-nodes-nebula- Install dependencies:
pnpm install- Build the node:
pnpm build- Restart n8n:
# If running n8n directly
n8n start
# If running via PM2
pm2 restart n8n
# If running via Docker
docker restart n8nInstallation with Docker
If you're running n8n in Docker, you can mount the custom nodes:
version: '3.8'
services:
n8n:
image: n8nio/n8n
volumes:
- ~/.n8n:/home/node/.n8n
- ./n8n-nodes-nebula:/home/node/.n8n/custom/n8n-nodes-nebula
environment:
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/customConfiguration
Setting up Credentials
- In n8n, go to Credentials → Add Credential
- Search for "Nebula API"
- Fill in the required fields:
| Field | Description | Example |
|-------|-------------|---------|
| Base URL | Your Nebula API backend base URL | https://api.mycompany.com/nebula |
| Username | Authentication username | n8n-service |
| Password | Authentication password | your-secure-password |
| Metadata | Additional JSON data for all requests | {"tenantId": "abc123"} |
Note: The webhook callback URL is automatically determined from your n8n instance configuration. You don't need to specify it manually.
Usage
Basic Workflow
- Add the Nebula HITL Request node to your workflow
- Connect it to your workflow flow
- Configure the node:
- Title: A short description of what needs human attention
- Message: Detailed markdown-formatted message
- Response Type: Choose from Ok, Yes/No, Text, or Form
Response Types
| Type | Description | Response Value |
|------|-------------|----------------|
| Ok | Simple acknowledgement | "ok" |
| Yes/No | Binary choice | "yes" or "no" |
| Text | Free-form text input | User's text input |
| Form (survey.json) | Custom form in survey.json format | Form submission data |
Form JSON Example
The Form response type uses survey.json format:
{
"elements": [
{
"type": "radiogroup",
"name": "decision",
"title": "Please select an option",
"choices": ["Approve", "Reject", "Need More Info"]
},
{
"type": "text",
"name": "comment",
"title": "Additional comments"
}
]
}Node Options
| Option | Description | Default |
|--------|-------------|---------|
| Priority | Request priority level | normal |
| Timeout (Minutes) | Auto-timeout for requests | 0 (no timeout) |
| Assignee | Email/ID to assign request to | (none) |
| Tags | Comma-separated tags | (none) |
Additional Data
You can pass additional JSON data that will be included in the request to your backend:
{
"orderId": "12345",
"customerName": "John Doe",
"amount": 150.00
}Backend API Requirements
Your backend API needs to implement the following endpoint:
POST /requests
Creates a new HITL request.
Request Body:
{
"requestId": "uuid-v4-string",
"title": "Approval Required",
"message": "Please review this order...",
"responseType": "yesno",
"form": null,
"webhookUrl": "https://your-n8n.com/webhook-waiting/xxx/nebula-hitl-response",
"priority": "normal",
"timeoutMinutes": 0,
"assignee": "[email protected]",
"tags": ["urgent", "finance"],
"metadata": {"tenantId": "abc123"},
"additionalData": {"orderId": "12345"},
"inputData": {},
"workflowId": "workflow-id",
"executionId": "execution-id",
"createdAt": "2024-01-15T10:30:00Z"
}Response:
{
"success": true,
"requestId": "uuid-v4-string",
"status": "pending"
}Webhook Callback (IMPORTANT - This Resumes the Workflow!)
When a human responds, your backend MUST call the webhookUrl provided in the request payload. This is what resumes the waiting n8n workflow execution.
POST to webhookUrl:
{
"requestId": "uuid-v4-string",
"response": "approved",
"responseValue": "approved",
"respondedBy": "[email protected]",
"respondedAt": "2024-01-15T11:45:00Z",
"comment": "Looks good!",
"data": {
"anyAdditionalData": "you want to pass"
}
}What happens:
- n8n receives the webhook POST
- The waiting workflow execution resumes
- The Nebula HITL Request node outputs the webhook payload data
- Subsequent nodes in your workflow can access
$json.response,$json.respondedBy, etc.
The webhook URL format: https://your-n8n-instance.com/webhook-waiting/{executionId}/nebula-hitl-response
The webhookUrl is automatically constructed and included in the request payload sent to your backend. Your backend simply needs to POST to this URL when a human responds.
Health Check (Optional)
For credential testing, implement:
GET /health
{
"status": "ok"
}Example Backend Implementation
Here's a minimal Express.js backend example:
const express = require('express');
const app = express();
app.use(express.json());
// Store requests in memory (use a database in production)
const requests = new Map();
// Create request
app.post('/requests', (req, res) => {
const { requestId, webhookUrl, ...data } = req.body;
requests.set(requestId, {
...data,
requestId,
webhookUrl,
status: 'pending'
});
console.log('New Nebula HITL request:', requestId, data.title);
res.json({ success: true, requestId, status: 'pending' });
});
// Respond to request (called by your UI)
app.post('/requests/:requestId/respond', async (req, res) => {
const requestId = req.params.requestId;
const { response, respondedBy } = req.body;
const request = requests.get(requestId);
if (!request) {
return res.status(404).json({ error: 'Request not found' });
}
// Call n8n webhook to resume workflow
await fetch(request.webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
requestId,
response,
responseValue: response,
respondedBy,
respondedAt: new Date().toISOString()
})
});
requests.delete(requestId);
res.json({ success: true });
});
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(3000, () => {
console.log('Nebula HITL Backend running on port 3000');
});Workflow Example
[Trigger] → [Process Data] → [Nebula HITL Request] → [If Response == "approved"] → [Continue]
↓
[Handle Rejection]Accessing Response Data
After the Nebula HITL Request node resumes (when the webhook is called), the node outputs the webhook payload data. You can access:
// In a Code node or expression
const requestId = $json.requestId; // The original request ID
const response = $json.response; // The response value (e.g., "approved", "yes", "rejected")
const responseValue = $json.responseValue; // Same as response (for compatibility)
const respondedBy = $json.respondedBy; // Who responded (email/ID)
const respondedAt = $json.respondedAt; // ISO timestamp when they responded
const comment = $json.comment; // Optional comment from responder
const data = $json.data; // Any additional data from your backendExample: Using in an IF node condition:
{{ $json.response }} equals "approved"Example: Send notification with response:
The request was {{ $json.response }} by {{ $json.respondedBy }} at {{ $json.respondedAt }}Development
Project Structure
n8n-nodes-nebula/
├── credentials/
│ └── NebulaApi.credentials.ts
├── nodes/
│ └── NebulaHitlRequest/
│ ├── NebulaHitlRequest.node.ts
│ ├── NebulaHitlRequest.node.json
│ └── nebulaHitlRequest.svg
├── package.json
├── tsconfig.json
└── README.mdBuilding from Source
# Install dependencies
pnpm install
# Build
pnpm build
# Watch mode (for development)
pnpm dev
# Lint
pnpm lint
# Format
pnpm formatTroubleshooting
Node not appearing in n8n
- Ensure the build completed successfully (
pnpm build) - Check that the
distfolder contains compiled JS files - Verify the custom nodes path in n8n configuration
- Restart n8n completely
Webhook not being called
- Ensure your n8n instance is publicly accessible or your backend can reach it
- Check the webhookUrl in your backend logs
- Verify the requestId matches in both the request and response
Authentication errors
- Verify credentials in n8n are correct
- Check your backend's authentication logic
- Look at the backend logs for more details
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
License
MIT License - see LICENSE file for details.
Related Projects
- n8n - Fair-code workflow automation
- n8n documentation on creating nodes
Support
- Create an issue on GitHub for bug reports
- Discussions for questions and feature requests
