Files
venus/dbus-no-foreign-land/NFL_API_REFERENCE.md
dev 9756538f16 Initial commit: Venus OS boat addons monorepo
Organizes 11 projects for Cerbo GX/Venus OS into a single repository:
- axiom-nmea: Raymarine LightHouse protocol decoder
- dbus-generator-ramp: Generator current ramp controller
- dbus-lightning: Blitzortung lightning monitor
- dbus-meteoblue-forecast: Meteoblue weather forecast
- dbus-no-foreign-land: noforeignland.com tracking
- dbus-tides: Tide prediction from depth + harmonics
- dbus-vrm-history: VRM cloud history proxy
- dbus-windy-station: Windy.com weather upload
- mfd-custom-app: MFD app deployment package
- venus-html5-app: Custom Victron HTML5 app fork
- watermaker: Watermaker PLC control UI

Adds root README, .gitignore, project template, and per-project
.gitignore files. Sensitive config files excluded via .gitignore
with .example templates provided.

Made-with: Cursor
2026-03-16 17:04:16 +00:00

6.6 KiB
Raw Blame History

NFL (No Foreign Land) API Reference for Location & Tracking

This document describes how the No Foreign Land (NFL) API works for submitting boat location and tracking data. Use it to build your own simpler module that does the same thing.


Overview

The NFL API accepts track data (sequences of GPS points) via a single HTTP POST endpoint. Each submission includes:

  1. A boat API key (user-specific, from noforeignland.com)
  2. A timestamp (milliseconds since epoch)
  3. A track (array of [timestamp_ms, lat, lon] points)

API Endpoint

Property Value
URL https://www.noforeignland.com/home/api/v1/boat/tracking/track
Method POST
Content-Type application/x-www-form-urlencoded

Authentication

1. Plugin API Key (hardcoded)

The Signal K plugin uses a fixed plugin API key sent in the header:

X-NFL-API-Key: 0ede6cb6-5213-45f5-8ab4-b4836b236f97

This identifies the plugin/client to the NFL backend. Your own module may need to use the same key or obtain a different one from NFL.

2. Boat API Key (user-specific)

Each boat/user has a Boat API Key from noforeignland.com:

  • Where to get it: Account > Settings > Boat tracking > API Key (on the website only, not in the app)
  • How its sent: As a form field boatApiKey in the POST body

Request Format

POST Body (form-urlencoded)

Field Type Required Description
timestamp string Yes Unix timestamp in milliseconds (epoch of the last track point)
track string Yes JSON string of track points (see below)
boatApiKey string Yes Users boat API key from noforeignland.com

Track format

track is a JSON string (so it must be serialized before being sent as form data). Example:

[
  [1234567890000, 52.1234, 4.5678],
  [1234567896000, 52.1240, 4.5680],
  [1234567902000, 52.1245, 4.5685]
]

Each point is a 3-element array:

Index Type Description
0 number Unix timestamp in milliseconds
1 number Latitude (WGS84, -90 to 90)
2 number Longitude (WGS84, -180 to 180)

Points should be ordered by time (oldest first).


Response Format

{
  "status": "ok",
  "message": "optional message"
}

Or on error:

{
  "status": "error",
  "message": "Error description"
}

HTTP Status Codes

  • 200: Request accepted. Check status in the JSON body for success/failure.
  • 4xx: Client error (e.g. invalid API key, bad request). Do not retry.
  • 5xx: Server error. Retry with backoff.

Minimal Example (cURL)

curl -X POST "https://www.noforeignland.com/home/api/v1/boat/tracking/track" \
  -H "X-NFL-API-Key: 0ede6cb6-5213-45f5-8ab4-b4836b236f97" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "timestamp=1234567902000" \
  -d "track=[[1234567890000,52.1234,4.5678],[1234567896000,52.1240,4.5680],[1234567902000,52.1245,4.5685]]" \
  -d "boatApiKey=YOUR_BOAT_API_KEY"

Minimal Example (JavaScript/Node.js)

const track = [
  [Date.now() - 60000, 52.1234, 4.5678],   // 1 min ago
  [Date.now() - 30000, 52.1240, 4.5680],   // 30 sec ago
  [Date.now(), 52.1245, 4.5685]             // now
];

const lastTimestamp = track[track.length - 1][0];

const params = new URLSearchParams();
params.append('timestamp', String(lastTimestamp));
params.append('track', JSON.stringify(track));
params.append('boatApiKey', 'YOUR_BOAT_API_KEY');

const response = await fetch('https://www.noforeignland.com/home/api/v1/boat/tracking/track', {
  method: 'POST',
  headers: {
    'X-NFL-API-Key': '0ede6cb6-5213-45f5-8ab4-b4836b236f97',
  },
  body: params,
});

const result = await response.json();
if (response.ok && result.status === 'ok') {
  console.log('Track sent successfully');
} else {
  console.error('Error:', result.message || response.statusText);
}

Minimal Example (Python)

import requests
import json
import time

track = [
    [int(time.time() * 1000) - 60000, 52.1234, 4.5678],   # 1 min ago
    [int(time.time() * 1000) - 30000, 52.1240, 4.5680],   # 30 sec ago
    [int(time.time() * 1000), 52.1245, 4.5685]             # now
]

last_timestamp = track[-1][0]

response = requests.post(
    'https://www.noforeignland.com/home/api/v1/boat/tracking/track',
    headers={'X-NFL-API-Key': '0ede6cb6-5213-45f5-8ab4-b4836b236f97'},
    data={
        'timestamp': last_timestamp,
        'track': json.dumps(track),
        'boatApiKey': 'YOUR_BOAT_API_KEY'
    }
)

result = response.json()
if response.ok and result.get('status') == 'ok':
    print('Track sent successfully')
else:
    print('Error:', result.get('message', response.text))

Data Validation (from the Signal K plugin)

Position validation

  • Latitude: -90 to 90
  • Longitude: -180 to 180
  • Reject non-numeric, NaN, Infinity
  • Reject positions near (0, 0) (likely GPS init values)

Velocity filter (optional)

To catch GPS outliers, the plugin rejects points that imply movement faster than a threshold (default 50 m/s ≈ 97 knots):

velocity = distance_meters / time_delta_seconds

If velocity > maxVelocity, the point is dropped.


24h Keepalive

The plugin does not use a separate keepalive endpoint. Instead:

  • If a boat has not moved for 24 hours, it still saves a point (using the last known position).
  • That point is included in the next track upload.
  • The effect is that boats stay “active” on NFL even when stationary.

Retry Logic (from the plugin)

  • Retry up to 3 times on network errors or 5xx responses.
  • Do not retry on 4xx client errors.
  • Increase timeout per attempt (e.g. 30s, 60s, 90s).
  • Wait 246 seconds between retries.

Summary: Minimal Module Checklist

Requirement Details
Endpoint POST https://www.noforeignland.com/home/api/v1/boat/tracking/track
Header X-NFL-API-Key: 0ede6cb6-5213-45f5-8ab4-b4836b236f97
Body application/x-www-form-urlencoded with timestamp, track, boatApiKey
Track format JSON array of [timestamp_ms, lat, lon]
Success response.status === 200 and body.status === 'ok'

Source Files Reference

File Purpose
src/types/api.ts API URL, plugin key, response types
src/lib/TrackSender.ts HTTP request, retry logic, track parsing
src/lib/TrackLogger.ts Position collection, filtering, JSONL storage
src/types/position.ts Internal position format
src/utils/validation.ts Position and velocity checks