Tutorial: Build Your First Adapter
Build a working adapter from scratch that integrates a simulated door lock with SpaceOS.
What You'll Build
A Node.js Express server that implements all 7 adapter protocol endpoints for a virtual door lock.
Prerequisites
- Node.js 18+
- A SpaceOS sandbox account
- Basic knowledge of REST APIs
Step 1: Project Setup
mkdir my-adapter && cd my-adapter
npm init -y
npm install express
Step 2: Implement the Server
Create server.js:
const express = require('express');
const app = express();
app.use(express.json());
const API_KEY = process.env.API_KEY || 'my-adapter-secret';
let doorLocked = true;
// Auth middleware
function auth(req, res, next) {
if (req.headers['x-api-key'] !== API_KEY) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
}
// 1. GET /ping — Health check
app.get('/ping', auth, (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString(), version: '2.0' });
});
// 2. GET /capabilities — Declare features
app.get('/capabilities', auth, (req, res) => {
res.json({
adapter: { name: 'My First Adapter', version: '1.0.0', vendor: 'Tutorial', protocol: '2.0' },
devices: [{
device_id: 'demo-lock-001',
name: 'Demo Door Lock',
type: 'lock',
model: 'Virtual Lock',
brand: 'Tutorial',
criticality: 'CRITICAL',
actions: ['unlock', 'lock', 'status'],
webhook_capable: false,
html_embed: true
}]
});
});
// 3. GET /status — Device state
app.get('/status', auth, (req, res) => {
res.json({
device_id: req.query.device_id,
online: true,
state: { locked: doorLocked, last_activity: new Date().toISOString() }
});
});
// 4. POST /action — Execute command
app.post('/action', auth, (req, res) => {
const { action } = req.body;
if (action === 'unlock') { doorLocked = false; setTimeout(() => doorLocked = true, 5000); }
if (action === 'lock') doorLocked = true;
res.json({ success: true, action, device_id: req.query.device_id, result: { locked: doorLocked } });
});
// 5. GET /ui — HTML embed
app.get('/ui', auth, (req, res) => {
res.type('html').send(`
<div style="text-align:center;padding:20px;font-family:sans-serif">
<h3>Demo Lock</h3>
<p>Status: ${doorLocked ? 'Locked' : 'Unlocked'}</p>
<button onclick="fetch('/action?device_id=${req.query.device_id}',{method:'POST',headers:{'Content-Type':'application/json','x-api-key':'${API_KEY}'},body:JSON.stringify({action:'unlock'})}).then(()=>location.reload())"
style="padding:12px 24px;background:#4CAF50;color:white;border:none;border-radius:8px;cursor:pointer">
Unlock
</button>
</div>
`);
});
// 6. POST /webhook/register — Register for events
app.post('/webhook/register', auth, (req, res) => {
res.json({ registered: true, events: req.body.events || [] });
});
// 7. POST /webhook/:event — Receive events
app.post('/webhook/:event', auth, (req, res) => {
console.log(`Webhook event: ${req.params.event}`, req.body);
res.json({ received: true });
});
app.listen(3000, () => console.log('Adapter running on port 3000'));
Step 3: Run It
API_KEY=my-secret node server.js
Step 4: Test Locally
# Health check
curl -H "x-api-key: my-secret" http://localhost:3000/ping
# Capabilities
curl -H "x-api-key: my-secret" http://localhost:3000/capabilities
# Status
curl -H "x-api-key: my-secret" "http://localhost:3000/status?device_id=demo-lock-001"
# Unlock
curl -X POST -H "x-api-key: my-secret" -H "Content-Type: application/json" \
"http://localhost:3000/action?device_id=demo-lock-001" \
-d '{"action":"unlock"}'
Step 5: Register in SpaceOS
- Make your adapter reachable (Tailscale, ngrok, or public deployment)
- In the admin dashboard, navigate to a physical space → Adapters tab
- Click Add Adapter
- Enter:
https://your-adapter-url?device_id=demo-lock-001 - Enter your API key
- Click Preview Capabilities
- Confirm and onboard
What You Built
A fully functional SpaceOS adapter that:
- Responds to health checks
- Declares its capabilities
- Reports device status
- Executes lock/unlock commands
- Serves a guest-facing UI tile
- Supports webhook registration
Next Steps
- Tutorial: Multi-Device Adapter — Handle multiple devices
- Certification Process — Get your adapter certified