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.
Headers
Section titled “Headers”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 |
String to sign
Section titled “String to sign”Join five fields with a newline (\n), no trailing newline:
<METHOD>\n<request URI>\n<client_id>\n<timestamp>\n<nonce>METHODis upper case, for exampleGETorPOST.- The request URI is the path plus query string exactly as sent, for example
/api-gateway/v1/user/sitesor/api-gateway/v2/iot-shadow/device/02_53_4D_00_00_01/events?limit=20. The scheme and host are not included. timestampandnonceare the exact header values.
import hashlib, hmac, secrets, timefrom 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, }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"What the server checks
Section titled “What the server checks”X-Timestampis an integer within 5 minutes of the server clock, before or after. Keep your clock synchronised.client_idexists.- The signature matches, compared in constant time.
- 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
Section titled “Errors”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 |

