Communicating with Fostrom over HTTP
HTTP provides a simple request-response protocol for devices that need to communicate with Fostrom without maintaining a persistent connection. This is ideal for battery-powered devices or situations where keeping a connection open isn't feasible.
All HTTP communication must be over HTTPS (TLS) for security. Request and response payloads use JSON format.
Connection Details
The HTTP API is available at:
https://device.fostrom.dev/
Authentication
Every request to the API (except the root endpoint) requires authentication using these HTTP headers:
X-Fleet-ID: <fleet_id> # 8-character string
X-Device-ID: <device_id> # 10-character string
X-Device-Secret: <device_secret> # Starts with "FOS-" followed by 32 characters
Headers are case-insensitive. The device secret is case-sensitive, and should be stored securely on the device.
Response Headers
All authenticated requests include these response headers:
Cache-Control: no-store
Connection: keep-alive
Content-Type: application/json
API Endpoints
Basic Information
Root Endpoint
GET /
< Status: 200
< {
< "fostrom": true,
< "endpoint": "device",
< "latest_endpoint_version": 1
< }
No authentication required. Useful for verifying connectivity.
Device Information
GET /v1
< Status: 200
< {
< "fostrom": true,
< "endpoint": "device",
< "endpoint_version": 1,
< "device": {
< "fleet_id": "<fleet_id>",
< "device_id": "<device_id>"
< }
< }
Returns information about the authenticated device.
Device Operations
Send Heartbeat
POST /v1/heartbeat
< Status: 201
< {"ok": true}
Sends a heartbeat to indicate the device is alive. No request body required.
Send Datapoint
POST /v1/datapoint/:schema_name
Content-Type: application/json
> {
> "temperature": 25.5,
> "humidity": 60,
> ...any fields
> }
< Status: 201
< {"ok": true}
Sends sensor data or telemetry to Fostrom. The :schema_name parameter identifies the datapoint schema. The payload must be a non-empty JSON object with fields matching your configured schema.
Optional Headers:
Idempotency-Key: Ensures the same datapoint isn't processed twice if retried
Send Message
POST /v1/msg/:schema_name
Content-Type: application/json
> {
> "alert": "Temperature threshold exceeded",
> ...any fields
> }
< Status: 201
< {"ok": true}
Sends an event or notification message to Fostrom. The :schema_name parameter identifies the message schema. Unlike datapoints, messages can have empty payloads.
Optional Headers:
Idempotency-Key: Ensures the same message isn't processed twice if retried
Mailbox Operations
Devices can receive mail from Fostrom through their mailbox. Mail must be fetched and acknowledged. The mail's unique ID is a UUIDv7 ID, and the name is the schema name.
Check Next Message (HEAD)
HEAD /v1/mailbox/next
< Status: 200 (message available) or 204 (empty mailbox)
< X-Mailbox-Size: 5
< X-Mail-Name: command_schema
< X-Mail-Id: 00000000-0000-7000-8000-000000000000
Checks if there's a message without retrieving the payload. Useful for polling efficiently.
Fetch Next Message
GET /v1/mailbox/next
# When message available:
< Status: 200
< X-Mailbox-Size: 5
< X-Mail-Name: command_schema
< X-Mail-Id: 00000000-0000-7000-8000-000000000000
< {
< "command": "update_config",
< "parameters": {...}
< }
# When mailbox empty:
< Status: 204
< X-Mailbox-Size: 0
< X-Mail-Name:
< X-Mail-Id:
< null
Retrieves the next message from the device's mailbox. Messages remain in the mailbox until acknowledged, rejected, or requeued.
Acknowledge Message
PUT /v1/mailbox/ack/:mail_id
< Status: 200
< X-Mailbox-Size: 4
< {"ok": true}
Confirms successful processing of a message. The message is removed from the mailbox.
Reject Message
PUT /v1/mailbox/reject/:mail_id
< Status: 200
< X-Mailbox-Size: 4
< {"ok": true}
Indicates the message could not be processed. The message is removed from the mailbox.
Requeue Message
PUT /v1/mailbox/requeue/:mail_id
< Status: 200
< X-Mailbox-Size: 5
< {"ok": true}
Returns the message to the mailbox for later processing. Useful when temporarily unable to handle a message.
Server-Sent Events (SSE)
GET /v1/events
Accept: text/event-stream
< Status: 200
< Content-Type: text/event-stream
< event: connected
<
< event: new_mail
<
< event: keep_alive
< ...
Opens a server-sent event stream for real-time notifications. The connection stays open and receives:
connected: Initial confirmationnew_mail: Notification when a new mail arrives in the mailbox. You can then issue a request to/v1/mailbox/nextto fetch the actual mail.keep_alive: Periodic heartbeat every 30 seconds
This endpoint is useful for devices that need real-time updates but can't use WebSockets or MQTT.
Error Handling
All errors return a JSON response with an error field:
{
"error": "error_type",
"detail": "specific_reason", // For authentication errors
"msg": "Human-readable message" // For authentication errors
}
Status Codes
- 200 OK: Successful GET/PUT request
- 201 Created: Successful POST request (data/message sent)
- 204 No Content: Mailbox is empty
- 400 Bad Request: Invalid request format or parameters
- 401 Unauthorized: Authentication failed
- 404 Not Found: Endpoint or resource not found
- 500 Internal Server Error: Server-side error
Authentication Error Details
When authentication fails (401), the response includes a detail field:
invalid_fleet_id: Fleet ID format is invalidinvalid_device_id: Device ID format is invalidinvalid_device_secret: Device secret format is invalidinvalid_credentials: Credentials are malformedfleet_not_found: Fleet doesn't existdevice_not_found: Device doesn't existdevice_secret_incorrect: Device Secret is incorrectdevice_disabled: Device has been disabledunknown: Other authentication failure
Best Practices
Efficient Polling
For battery-powered devices checking for mail:
- Use
HEAD /v1/mailbox/nextto check for mail without downloading payloads - Only use
GET /v1/mailbox/nextwhen mail is available - Always acknowledge or handle mail promptly to avoid reprocessing
Idempotency
Use the Idempotency-Key header when sending critical data or messages to ensure they're processed only once, even if network issues cause retries. The Idempotency Key should be a valid recently-generated UUIDv7 36-character (or 32-character, without dashes) string.
Error Recovery
- Implement exponential backoff for retries on 500 errors
- For 401 errors, verify credentials before retrying
- For 400 errors, check request format - these won't succeed without changes
Payload Limits
- Maximum request size: 64 KB
- Keep payloads concise to reduce bandwidth usage
Example Usage
Sending Temperature Data
curl -X POST https://device.fostrom.dev/v1/datapoint/temperature \
-H "X-Fleet-ID: FLEET123" \
-H "X-Device-ID: DEVICE1234" \
-H "X-Device-Secret: FOS-12345678901234567890123456789012" \
-H "Content-Type: application/json" \
-d '{"celsius": 22.5, "timestamp": "2024-01-01T12:00:00Z"}'
Checking and Processing Messages
# Check if a mail available
curl -I https://device.fostrom.dev/v1/mailbox/next \
-H "X-Fleet-ID: FLEET123" \
-H "X-Device-ID: DEVICE1234" \
-H "X-Device-Secret: FOS-12345678901234567890123456789012"
# Fetch the next mail
curl https://device.fostrom.dev/v1/mailbox/next \
-H "X-Fleet-ID: FLEET123" \
-H "X-Device-ID: DEVICE1234" \
-H "X-Device-Secret: FOS-12345678901234567890123456789012"
# Acknowledge mail after processing
curl -X PUT https://device.fostrom.dev/v1/mailbox/ack/00000000-0000-7000-8000-000000000000 \
-H "X-Fleet-ID: FLEET123" \
-H "X-Device-ID: DEVICE1234" \
-H "X-Device-Secret: FOS-12345678901234567890123456789012"
WebSocket Upgrade
The HTTP endpoint also supports WebSocket upgrades for MQTT and Moonlight protocols. See the respective protocol documentation for details on using WebSockets.