Request Signing
Overview
For security reasons, some endpoints require request signing. Endpoints that require it say so in their description, for example Create a Transaction. A signed request proves that it was created by your OAuth application and was not changed or replayed on the way.
Signature Headers
To sign a request, send the following headers together with the usual Authorization: Bearer header:
X-Timestamp: Timestamp of the request in UNIX time (seconds)X-Nonce: A random, unique string (max. 40 characters)X-Signature-Algorithm:HMAC-SHA256X-Signature: The calculated signature as a lowercase hexadecimal string
Calculate the Signature
Build the signature content in the format below, then sign it with HMAC-SHA256 using your OAuth application secret.
$httpMethod $requestPathWithQuery
timestamp=$timestamp
nonce=$nonce
body_hash=sha256($body)$httpMethodis the uppercase HTTP method, for exampleGETorPOST$requestPathWithQueryis the request path, followed by?and the query string when the request has query parametersbody_hashis the SHA-256 hash of the request body as a lowercase hexadecimal string. Leave it empty when the request has no body, for example for GET requests
Note:
- Sort query parameters by name and encode them according to RFC 3986 (a space is encoded as
%20, not+) - The lines are separated by a single line feed (
\n) and there is no trailing line feed
Example
Imagine the following request and settings:
GET https://example.com/my/path?foo=bar&zzz=second+parameter- Timestamp is
1234567890(Feb 13 2009) - Nonce is
9b95be45f403af9c0039e08bee85f409611fa1b2 - Application secret is
my_key_123456789012345678901234567890123
The resulting signature content is:
GET /my/path?foo=bar&zzz=second%20parameter
timestamp=1234567890
nonce=9b95be45f403af9c0039e08bee85f409611fa1b2
body_hash=The calculated signature (using the above secret) is 88810379e74af556c15fad9c8fc9fc1756b000d3915df597b69056af0f3ff004.
If the same settings are used to send POST https://example.com/my/path with body {"foo":"bar","zzz":"second parameter"}, the body_hash is 40c9aa2af101ea84a90c0eb711d502277a6ca1bff772917a8b772f0efc0b1cde and the signature content is:
POST /my/path
timestamp=1234567890
nonce=9b95be45f403af9c0039e08bee85f409611fa1b2
body_hash=40c9aa2af101ea84a90c0eb711d502277a6ca1bff772917a8b772f0efc0b1cdeThe calculated signature is f0d36077e97b2f644d960cd60b1875c7dabfc1630d52c7538e5bd1b0012a75be.
The following Python snippet reproduces the GET example:
import hashlib
import hmac
app_secret = "my_key_123456789012345678901234567890123"
body = "" # raw request body, empty for GET requests
body_hash = hashlib.sha256(body.encode()).hexdigest() if body else ""
content = "\n".join([
"GET /my/path?foo=bar&zzz=second%20parameter",
"timestamp=1234567890",
"nonce=9b95be45f403af9c0039e08bee85f409611fa1b2",
f"body_hash={body_hash}",
])
signature = hmac.new(app_secret.encode(), content.encode(), hashlib.sha256).hexdigest()
print(signature) # 88810379e74af556c15fad9c8fc9fc1756b000d3915df597b69056af0f3ff004Validation Rules
Important:
- The access token must be issued to an OAuth application (see Authorization), because the signature is checked with that application's secret
X-Timestampmust be within 10 minutes of the server timeX-Noncemust be unique for your OAuth application. Never reuse a nonce, not even when you retry a request- Hash the exact body bytes you send. Re-serializing the JSON after signing changes the hash
If the validation fails, the API returns HTTP status 412 Precondition Failed with a message in the format Request signing failed. Reason: <reason>, for example Request signing failed. Reason: Nonce is not unique.