Request signature — HMAC-SHA256
The HMAC key is SecretKey, the message is the concatenation of SubscriptionProjectID and Now; the result is 64 lowercase hex characters.
signature = lowercase( HMAC_SHA256(SecretKey, SubscriptionProjectID + Now) )Now — UTC in the yyyyMMddHHmmss format. The Now and Signature fields are passed in the body of every request next to SubscriptionProjectID, not in HTTP headers.
The allowed time drift is ±20 minutes. On an invalid signature or an expired Now, the response contains StatusName=SECURITY_ERROR.
Computing the signature
Sandbox
curl
SECRET=subs_secret_key
PROJECT_ID=2001
NOW=$(date -u +%Y%m%d%H%M%S)
SIG=$(printf '%s%s' "$PROJECT_ID" "$NOW" \
| openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')
echo "$SIG"Test vectors
Run the vectors before the first network call: two hex strings that do not match are debugged locally in a minute, a server rejection is not. The full set is at subs_sigtest.json , including negative vectors: reversed concatenation (Now + SubscriptionProjectID) and the MD5 formula — if your signature is 32 characters instead of 64, you are computing it with legacy version 1, while the sandbox implements version 2 only.
Sandbox
{ "id": "subs-valid",
"subscription_project_id": "2001",
"secret_key": "subs_secret_key",
"now": "20260210175637",
"message": "200120260210175637",
"signature": "7b61d6943253d43f1318d9bf251d599f8e6d1a3608123b0178732d4828d436a6" }
{ "id": "subs-negative-reversed", "valid": false,
"why": "Now + SubscriptionProjectID concatenation — the order is reversed",
"signature": "b49249dc3accfa0e4a55a0b576ee0aac08394d87407e7283ddcb7df3b937b2dc" }The subscription formula is not the same as the one for one-time payments: a single HMAC pass, no login and no intermediate inner, its own secret. The negative vector catches both the one-time payments formula and the MD5 from old editions.