Serve HMAC
Serve HMAC
SDKs authenticate to the adserver with the public app key prefix plus HMAC-SHA256. Production and sandbox keys sign the same way. GET /v1/click/{token} does not use these headers.
POST
https://serve.meridianads.tech/v1/decisionLocal http://localhost:8090/v1/decision
Headers
| Header | Value |
|---|---|
| X-App-Key | Public prefix from key create, for example mk_live_… or mk_test_… |
| X-Timestamp | Unix seconds. Default allowed skew is 300 seconds. |
| X-Signature | Hex digest of HMAC-SHA256 over the canonical string. |
Canonical string
Sign exactly {unix_timestamp}.{METHOD}.{path}.{raw_body}. METHOD is the HTTP method as sent (POST). path is the URL path only, such as /v1/decision, with no query string. raw_body is the exact bytes on the wire. If JSON spacing differs from what you signed, the signature fails.
The secret returned at create time is hex. Decode it to 32 bytes before HMAC. Do not HMAC the hex string itself.
Node
Node
import { createHmac } from 'node:crypto'
const ts = String(Math.floor(Date.now() / 1000))
const method = 'POST'
const path = '/v1/decision'
const body = JSON.stringify({
placement: 'home',
user_key: 'user-42',
context: { country: 'US' }
})
const canonical = `${ts}.${method}.${path}.${body}`
const secret = Buffer.from(secretHex, 'hex')
const signature = createHmac('sha256', secret).update(canonical).digest('hex')
await fetch('https://serve.meridianads.tech/v1/decision', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-App-Key': prefix,
'X-Timestamp': ts,
'X-Signature': signature
},
body
})Python
Python
import hashlib, hmac, json, time, urllib.request
ts = str(int(time.time()))
method = "POST"
path = "/v1/decision"
body = json.dumps(
{"placement": "home", "user_key": "user-42", "context": {"country": "US"}},
separators=(",", ":"),
)
canonical = f"{ts}.{method}.{path}.{body}"
secret = bytes.fromhex(secret_hex)
signature = hmac.new(secret, canonical.encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(
"https://serve.meridianads.tech/v1/decision",
data=body.encode(),
method=method,
headers={
"Content-Type": "application/json",
"X-App-Key": prefix,
"X-Timestamp": ts,
"X-Signature": signature,
},
)