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 confirmation
  • new_mail: Notification when a new mail arrives in the mailbox. You can then issue a request to /v1/mailbox/next to 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 invalid
  • invalid_device_id: Device ID format is invalid
  • invalid_device_secret: Device secret format is invalid
  • invalid_credentials: Credentials are malformed
  • fleet_not_found: Fleet doesn't exist
  • device_not_found: Device doesn't exist
  • device_secret_incorrect: Device Secret is incorrect
  • device_disabled: Device has been disabled
  • unknown: Other authentication failure

Best Practices

Efficient Polling

For battery-powered devices checking for mail:

  1. Use HEAD /v1/mailbox/next to check for mail without downloading payloads
  2. Only use GET /v1/mailbox/next when mail is available
  3. 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.