Skip to content

Client signature

Server-to-server integrations that only need the devices of one SplashMe account can skip the OAuth2 flow. Each request carries a signature computed with the client secret; the gateway authenticates it as the account that registered the client. There is no sign-in page, no token and nothing to refresh.

Use the OAuth2 flow instead when your application acts for other users’ accounts.

Send all four headers on every request. A request with all four is authenticated by signature and any Authorization header is ignored; a request missing any of them falls back to bearer token authentication.

Header Value
X-Client-Id Your client_id
X-Timestamp Current Unix time in seconds
X-Nonce A random string unique to this request, at least 16 random bytes encoded
X-Signature Lower-case hex HMAC-SHA256 of the string to sign, keyed with client_secret

Join five fields with a newline (\n), no trailing newline:

<METHOD>\n<request URI>\n<client_id>\n<timestamp>\n<nonce>
  • METHOD is upper case, for example GET or POST.
  • The request URI is the path plus query string exactly as sent, for example /api-gateway/v1/user/sites or /api-gateway/v2/iot-shadow/device/02_53_4D_00_00_01/events?limit=20. The scheme and host are not included.
  • timestamp and nonce are the exact header values.
import hashlib, hmac, secrets, time
from urllib.parse import urlsplit
def signed_headers(client_id: str, client_secret: str, method: str, url: str) -> dict[str, str]:
parts = urlsplit(url)
request_uri = parts.path + (f"?{parts.query}" if parts.query else "")
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
message = "\n".join([method.upper(), request_uri, client_id, timestamp, nonce])
signature = hmac.new(client_secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return {
"X-Client-Id": client_id,
"X-Timestamp": timestamp,
"X-Nonce": nonce,
"X-Signature": signature,
}
Terminal window
curl https://api.splashmepool.com.au/api-gateway/v1/user/sites \
-H "X-Client-Id: $CLIENT_ID" -H "X-Timestamp: $TS" -H "X-Nonce: $NONCE" -H "X-Signature: $SIG"
  1. X-Timestamp is an integer within 5 minutes of the server clock, before or after. Keep your clock synchronised.
  2. client_id exists.
  3. The signature matches, compared in constant time.
  4. The nonce has not been used by this client in the last 5 minutes. A nonce is consumed as soon as the signature verifies, so generate a fresh one for every attempt, including retries.

The request then runs as the client’s owner with the same permissions as a bearer token for that account, including device ownership checks.

Errors use the standard {"status": "FAILED", "message": …} envelope.

Status message Cause
401 Invalid X-Timestamp Not an integer
401 Request timestamp out of allowed window More than 5 minutes from the server clock
401 Unknown client_id No such client
401 Invalid signature Wrong secret or a mismatch in the string to sign
401 Replay detected Nonce already used within the window
500 Signature verification unavailable The replay store is unavailable; retry later with a fresh nonce