- Backend: Update Legacy API credentials to robster/rob2 - Frontend: ROOM_LOCATIONS with icons and legacyCode mapping - Device Settings: Modal picker for room selection - api.ts: Bidirectional conversion (code ↔ name) - Various UI/UX improvements across screens PRD-DEPLOYMENT.md completed (Score: 9/10) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
22 KiB
API Optimization Proposals for WellNuo Integration
Note: This document is for discussion purposes only, not final requirements. All proposals are open for discussion — we're happy to consider alternative approaches and adapt to your system's capabilities. Let's schedule a call to discuss the details if needed.
1. Introduction
1.1 Context
We're developing the WellNuo mobile application with our own database of users and beneficiaries. For working with IoT devices (sensors), we need integration with your Legacy API.
1.2 Current Integration Architecture
┌─────────────────┐ ┌──────────────────┐ ┌────────────────────┐
│ WellNuo App │ ──▶ │ WellNuo Backend │ ──▶ │ eluxnetworks.net │
│ (React Native) │ │ (Node.js) │ │ Legacy API │
└─────────────────┘ └──────────────────┘ └────────────────────┘
│
▼
┌──────────────┐
│ PostgreSQL │
│ (WellNuo) │
└──────────────┘
1.3 Summary of Request
The current API workflow requires creating a user in your database to obtain an authorization token. However:
- Users already exist in our DB — creating them in your DB is redundant
- We don't use these users — they're only needed to obtain a token
- This creates "garbage" records — unused accounts accumulate in your DB
Request: Add the ability for service integration via API Key without creating users.
1.4 Note on Current Capabilities
Technically, we can create users via the new_user_form endpoint:
POST /function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=new_user_form
&firstName=John
&lastName=Doe
&email=john@example.com
&password=password123
&devices=[]
&agreementDate=2025-01-23
&privacyPolicyVersion=1.0
&phone=+1234567890
However, we consider this a legacy approach for the following reasons:
- Users aren't used as intended — we create them solely to obtain an authorization token, not for actual use in your system
- Data duplication — user information is stored in two databases without synchronization
- "Garbage" records — accounts accumulate in your DB that will never be used directly
- Password transmitted in plaintext — security issue (see section 2)
Proposal: Schedule a separate meeting to discuss and approve the integration architecture.
1.5 Which Endpoints We Actually Need
We NEED (minimum set):
| Endpoint | Purpose |
|---|---|
set_deployment |
Create deployment and link devices to beneficiary |
messages_age |
Sensor status: when last online (online/offline) |
device_set_well_id |
Link device by well_id |
device_form |
Configure sensor location (room) |
device_list_by_deployment |
Get list of deployment's devices |
We DON'T NEED:
| Endpoint | Why Not Needed |
|---|---|
get_raw_data, get_presence_data |
Raw sensor data — processed on your side |
dashboard_* |
Dashboard is shown through Julia AI |
store_alarms, send_walarm |
Alarms are managed by your system |
get_sensor_* |
Charts and analytics — not our task |
request_node_red, store_flow |
We don't use Node-RED |
Conclusion: We only need CRUD for deployments/devices and sensor status. All sensor data and analytics remain on your side.
2. Security Issues with Current API
2.1 Critical Issues
| # | Issue | Severity | Description |
|---|---|---|---|
| 1 | Password in request body | 🔴 Critical | credentials accepts ps (password) in POST body |
| 2 | Plaintext beneficiary password | 🔴 Critical | set_deployment requires beneficiary_password |
| 3 | No API Key auth | 🟡 High | No service authorization mechanism |
| 4 | Redundant user_name |
🟡 Medium | Every request requires user_name + token, even though token already identifies user |
2.2 Issue #1 Details: Password in Request Body
Current request:
POST /function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=credentials&user_name=anandk&ps=anandk_8&clientId=001&nonce=1234567890
Risks:
- Password ends up in server access logs
- Password may end up in proxy/WAF/CDN logs
- When logging POST body — full credential compromise
2.3 Issue #2 Details: Plaintext Password When Creating Deployment
Current request:
POST /function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=set_deployment
&user_name=anandk
&token=eyJ...
&deployment=NEW
&beneficiary_user_name=john_doe
&beneficiary_password=password123 ← PLAINTEXT PASSWORD
&beneficiary_email=john@example.com
...
Risks:
- We're forced to know/generate user's password
- Password transmitted in plain text
- When logged — credential compromise
2.4 Issue #4 Details: Redundant user_name
Current request (any endpoint):
function=device_list
&user_name=anandk ← why, if there's a token?
&token=eyJ... ← token already contains user_id
&first=0
&last=100
Issue: If the JWT token already contains the user identifier, passing user_name is redundant.
Potential risk: Token confusion attack — using one user's token with another user's user_name. Server should verify the match, but this is an additional failure point.
3. Our Security Standard (WellNuo API)
For reference, here's how authorization works in our API:
3.1 Authorization via Bearer Token in Header
GET /api/auth/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
3.2 Why This Is More Secure
| Aspect | Body (current) | Header (proposed) |
|---|---|---|
| Logging | Often logged | Usually masked |
| Caching | May be cached | Not cached |
| CSRF protection | Vulnerable | Protected |
| Standard | Non-standard | RFC 6750 |
4. Requested Changes
4.1 Authorization Mechanism: Options
Requirement: Replace current flow with password in body with a more secure option.
Current flow (problematic):
1. POST credentials (user_name + password in body) ← password in request body
2. Get JWT token (valid 7 days)
3. Use token + user_name in every request
4. Repeat steps 1-2 when token expires
Option A: API Key (static key)
1. You generate an API Key for our integration (once)
2. We use the key in header: Authorization: X-API-Key sk-wellnuo-xxx
3. When revocation needed — deactivate key and issue a new one
Pros: simplicity, no refresh logic, easy audit Cons: one key for entire integration
Option B: Service Account (token in header)
1. You create a service account for WellNuo (once)
2. We get token via credentials, but pass IT IN HEADER
3. Format: Authorization: Bearer eyJ...
4. user_name not needed in request body (token already identifies)
Pros: familiar JWT flow, can use existing infrastructure Cons: need refresh token
Main requirement (both options):
- Token/key in header
Authorization, not in body - Remove
user_namefrom request body (redundant)
4.2 Endpoint: Authorization
CURRENT REQUEST ❌
POST https://eluxnetworks.net/function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=credentials
&user_name=anandk
&ps=anandk_8
&clientId=001
&nonce=1234567890
Response:
{
"status": "200 OK",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 604800
}
PROPOSED REQUEST ✅
Option A: No longer needed (API Key doesn't require token request)
Option B: If key validation needed
POST https://eluxnetworks.net/function/well-api/api
Authorization: X-API-Key sk-wellnuo-xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"function": "validate_api_key"
}
Expected response:
{
"status": "200 OK",
"valid": true,
"client_name": "WellNuo App",
"permissions": ["deployments", "devices", "read_data"]
}
4.3 Endpoint: Create Deployment
CURRENT REQUEST ❌
POST https://eluxnetworks.net/function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=set_deployment
&user_name=anandk
&token=eyJ...
&deployment=NEW
&beneficiary_name=John Doe
&beneficiary_email=john@example.com
&beneficiary_user_name=john_doe ← creates user in your DB
&beneficiary_password=password123 ← plaintext password
&beneficiary_address=123 Main St
&caretaker_username=jane_doe ← creates another user
&caretaker_email=jane@example.com
&persons=1
&gender=Male
&race=0
&born=1955
&pets=0
&lat=40.7128
&lng=-74.0060
&wifis=["HomeWiFi|password123"]
&devices=[497,523]
Response:
{
"status": "200 OK",
"deployment_id": 42,
"beneficiary_user_id": 156,
"caretaker_user_id": 157
}
PROPOSED REQUEST ✅
POST https://eluxnetworks.net/function/well-api/api
Authorization: Bearer eyJ... (or X-API-Key sk-wellnuo-xxx)
Content-Type: application/json
{
"function": "set_deployment",
"deployment": "NEW",
"external_beneficiary_id": "clxyz123-uuid-from-our-db",
"beneficiary_name": "John Doe",
"beneficiary_email": "john@example.com",
"devices": [497, 523]
}
Note: Only minimally required fields. Others (address, lat/lng, persons, pets, gender, born) — optional, we can pass them if needed.
Expected response:
{
"status": "200 OK",
"deployment_id": 42,
"external_beneficiary_id": "clxyz123-uuid-from-our-db"
}
Key differences:
| Aspect | Current | Proposed |
|---|---|---|
| Authorization | token + user_name in body | Token/key in header |
| User creation | beneficiary_user_name + beneficiary_password |
NOT created |
| Beneficiary ID | Generated in your DB | external_beneficiary_id from our DB |
| Password | Transmitted plaintext | Not required |
4.4 Endpoint: Link Device to Deployment
CURRENT REQUEST ❌
POST https://eluxnetworks.net/function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=device_set_well_id
&user_name=anandk
&token=eyJ...
&device_id=1234
&well_id=497
&mac=81A14C
Response:
{
"status": "200 OK",
"success": true
}
PROPOSED REQUEST ✅
POST https://eluxnetworks.net/function/well-api/api
Authorization: Bearer eyJ... (or X-API-Key)
Content-Type: application/x-www-form-urlencoded
function=device_set_well_id
&device_id=1234
&well_id=497
&mac=81A14C
Change: Remove user_name and token from body, authorization via header.
4.5 Endpoint: Update Device Metadata
CURRENT REQUEST ❌
POST https://eluxnetworks.net/function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=device_form
&user_name=anandk
&token=eyJ...
&well_id=497
&device_mac=81A14C
&description=Living Room Sensor
&location=103
PROPOSED REQUEST ✅
POST https://eluxnetworks.net/function/well-api/api
Authorization: Bearer eyJ... (or X-API-Key)
Content-Type: application/x-www-form-urlencoded
function=device_form
&well_id=497
&device_mac=81A14C
&description=Living Room Sensor
&location=103
Change: Remove user_name and token from body, authorization via header.
4.6 Endpoint: List Deployment Devices
CURRENT REQUEST ❌
POST https://eluxnetworks.net/function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=device_list_by_deployment
&user_name=anandk
&token=eyJ...
&deployment_id=42
&first=0
&last=100
PROPOSED REQUEST ✅
POST https://eluxnetworks.net/function/well-api/api
Authorization: Bearer eyJ... (or X-API-Key)
Content-Type: application/x-www-form-urlencoded
function=device_list_by_deployment
&deployment_id=42
&first=0
&last=100
Change: Remove user_name and token from body, authorization via header.
4.7 Endpoint: Sensor Status (online/offline)
We need this endpoint — we show users when the sensor was last online.
CURRENT REQUEST ❌
POST https://eluxnetworks.net/function/well-api/api
Content-Type: application/x-www-form-urlencoded
function=messages_age
&clientId=wellnuo_001
&user_name=anandk
&token=eyJ...
&nonce=12345
&macs=64B70888FAD4,64B708890F80,64B708890898
PROPOSED REQUEST ✅
POST https://eluxnetworks.net/function/well-api/api
Authorization: Bearer eyJ... (or X-API-Key)
Content-Type: application/x-www-form-urlencoded
function=messages_age
&clientId=wellnuo_001
&nonce=12345
&macs=64B70888FAD4,64B708890F80,64B708890898
Change: Remove user_name and token from body, authorization via header.
5. Summary of Changes
5.1 Changes by Endpoint
| Endpoint | Current | What to Remove | What to Add |
|---|---|---|---|
| All endpoints | user_name + token in body |
user_name, token from body |
Authorization header |
set_deployment |
Creates user | beneficiary_user_name, beneficiary_password |
external_beneficiary_id |
5.2 List of Endpoints We Use
| # | Endpoint | Purpose | Change |
|---|---|---|---|
| 1 | credentials |
Get token | Token in header (or API Key) |
| 2 | set_deployment |
Create deployment | + external_beneficiary_id, no password |
| 3 | device_set_well_id |
Link device | Auth in header |
| 4 | device_form |
Configure sensor (room) | Auth in header |
| 5 | device_list_by_deployment |
List devices | Auth in header |
| 6 | messages_age |
Sensor status (online/offline) | Auth in header |
5.3 Room Codes (location) — Reference
| Code | Name |
|---|---|
| 102 | Bedroom |
| 103 | Living Room |
| 104 | Kitchen |
| 105 | Bathroom |
| 106 | Hallway |
| 107 | Office |
| 108 | Garage |
| 109 | Dining Room |
| 110 | Basement |
| 200 | Other |
6. Critical Bug: set_deployment Does Not Return deployment_id
6.1 Problem Description
When successfully creating a deployment via set_deployment, the API returns a minimal response without the created record's identifier.
Current response:
{"ok": 1, "status": "200 OK"}
Required response:
{"ok": 1, "deployment_id": 78, "status": "200 OK"}
6.2 Why This Is Critical for Integration
┌─────────────────────────────────────────────────────────────────────────────┐
│ CURRENT FLOW (BROKEN) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. WellNuo creates beneficiary in its DB │
│ → beneficiary_id = "clxyz123-uuid" │
│ │
│ 2. WellNuo calls set_deployment in Legacy API │
│ → Deployment created successfully │
│ → Response: {"ok": 1, "status": "200 OK"} │
│ → deployment_id = ??? ← UNKNOWN! │
│ │
│ 3. WellNuo CANNOT save the link: │
│ beneficiary_deployments.legacy_deployment_id = NULL │
│ │
│ 4. WellNuo CANNOT get sensors for this beneficiary: │
│ device_list_by_deployment requires deployment_id │
│ → INTEGRATION IMPOSSIBLE │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
6.3 Workaround Attempts (unsuccessful)
We tried to find the created deployment through search:
# Attempt: find recently created deployments
curl -X POST "..." -d "function=find_deployments" -d "well_ids=70,71,72,73,74,75"
Why this doesn't work:
- Race condition with parallel requests from different clients
- IDs not guaranteed to be sequential
- Requires additional API call
- Unreliable in production environment
6.4 Working Request (for reproduction)
#!/bin/bash
TOKEN="<your_token>"
TS=$(date +%s)
curl -s -X POST "https://eluxnetworks.net/function/well-api/api" \
-d "function=set_deployment" \
-d "user_name=robster" \
-d "token=$TOKEN" \
-d "deployment=NEW" \
-d "beneficiary_name=WellNuo Test" \
-d "beneficiary_email=wellnuo-test-${TS}@wellnuo.app" \
-d "beneficiary_user_name=wellnuo_test_${TS}" \
-d "beneficiary_password=wellnuo123" \
-d "beneficiary_address=WellNuo App" \
-d "caretaker_username=anandk" \
-d "caretaker_email=anandk@wellnuo.app" \
-d "firstName=WellNuo" \
-d "lastName=Test" \
-d "persons=1" \
-d "pets=0" \
-d "gender=0" \
-d "race=0" \
-d "born=1960" \
-d "lat=0" \
-d "lng=0" \
-d "wifis=[]" \
-d "devices=[]"
# Response: {"ok": 1, "status": "200 OK"}
# Expected: {"ok": 1, "deployment_id": 78, "status": "200 OK"}
6.5 Required Fix
Modify the set_deployment function to return the created deployment ID:
{
"ok": 1,
"deployment_id": 78,
"status": "200 OK"
}
Priority: 🔴 Blocker — integration is impossible without this fix.
7. Changes on WellNuo App Backend Side (what we plan to implement)
After agreeing on the new architecture, we plan to implement the following on the WellNuo App Backend side (the backend supporting the mobile application):
7.1 Planned Changes
| # | Change | Description |
|---|---|---|
| 1 | API Proxy Layer | All Legacy API calls go through WellNuo backend, not directly from mobile app |
| 2 | Secure Key Storage | API Key stored in Doppler (secrets manager), not in code or .env files |
| 3 | Retry Logic | Automatic retries for temporary errors (5xx, timeout) |
| 4 | Error Handling | Graceful degradation — app works even if Legacy API is temporarily unavailable |
| 5 | Audit Logging | Logging all Legacy API calls for debugging and monitoring |
7.2 Architecture After Changes
┌─────────────────┐ ┌──────────────────┐ ┌────────────────────┐
│ WellNuo App │ │ WellNuo Backend │ │ eluxnetworks.net │
│ (React Native) │ │ (Node.js) │ │ Legacy API │
└────────┬────────┘ └────────┬─────────┘ └─────────┬──────────┘
│ │ │
│ 1. Create beneficiary │ │
│ ───────────────────────▶ │
│ │ │
│ │ 2. set_deployment │
│ │ (API Key in header) │
│ │ ─────────────────────────▶
│ │ │
│ │ 3. {deployment_id: 78} │
│ │ ◀─────────────────────────
│ │ │
│ │ 4. Save mapping: │
│ │ beneficiary_id ↔ │
│ │ deployment_id │
│ │ │
│ 5. Success │ │
│ ◀─────────────────────── │
│ │ │
7.3 Mapping Table (WellNuo DB)
-- Link between WellNuo beneficiary and Legacy deployment
CREATE TABLE beneficiary_deployments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
beneficiary_id UUID REFERENCES person_details(id),
legacy_deployment_id INTEGER NOT NULL, -- ID from eluxnetworks API
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(beneficiary_id)
);
8. Priorities
8.1 What's Needed for MVP (blockers)
| # | Requirement | Priority | Status |
|---|---|---|---|
| 1 | set_deployment returns deployment_id |
🔴 Blocker | Awaiting fix |
| 2 | API Key or Service Account authorization | 🔴 Blocker | Awaiting decision |
8.2 What Can Be Done Later
| # | Requirement | Priority |
|---|---|---|
| 1 | Remove user_name from body |
🟡 Medium |
| 2 | Move token to header | 🟡 Medium |
| 3 | external_beneficiary_id instead of creating users |
🟢 Nice to have |
If you have any questions or comments — let's schedule a call.