Introduction
This section explains how to integrate the optional payload signature mechanism, which guarantees the integrity and authenticity of your API requests.
The signature is checked on the following endpoints only:
POST /payment-intents
POST /payment-intents/:id/payments and POST /payment-intents/:id/payments/sync
POST /transfers
POST /irt/transfer and POST /irt/transfer/verified-recipient
POST /terminal/payments
POST /delegated/payments, POST /delegated/payments/sync, POST /delegated/transfers and POST /delegated/transfers/irt
Other endpoints, such as refunds, deposits, payment links and submerchants, do not check the signature.
Prerequisites
Before using the payload signature, complete the following steps:
- Generate an RSA key pair. The private key signs the request body; the public key lets HUB2 verify the signature. Keep the private key secret.
- Upload the public key to your merchant preferences in the HUB2 dashboard. The dashboard accepts either a PEM-encoded RSA public key (
-----BEGIN PUBLIC KEY-----) or a PEM-encoded X.509 certificate holding that key (-----BEGIN CERTIFICATE-----). Two keys can be registered, a current one and a previous one, so that you can rotate keys without interruption. Contact the support team if you need help with this step.
Signing a request
- Serialize the JSON request body in compact form (no spaces or line breaks) and send exactly this string as the body.
- Sign this string with your private key using RSA with SHA-256 (
RSA-SHA256, PKCS#1 v1.5).
- Encode the signature in Base64 and send it in the
X-Signature header.
HUB2 does not verify the raw bytes it receives: it parses your body and recomputes the signed string the way JavaScript’s JSON.stringify does, with the same key order, no whitespace, non-ASCII characters and / left unescaped, and numbers in their shortest form (1000, not 1000.0). A body sent with indentation, extra spaces or a different escaping therefore fails verification. Sign that exact string, for example with Python json.dumps(body, separators=(',', ':'), ensure_ascii=False) or PHP json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES). In Python, pass whole amounts as integers (int(amount)): json.dumps writes a float such as 1000.0 with its .0.
Sandbox environment
During development and testing, with the Environment: sandbox header:
- If the
X-Signature header is present, HUB2 verifies it with the public keys registered for your account.
- If the signature is valid, the request is processed normally.
- If the signature is invalid or malformed, the API returns an HTTP
400 error with the code signature_error.
- If the
X-Signature header is absent, the request is processed without signature verification.
Live environment
To enforce signature verification for live transactions (Environment: live), enable the option that forces traffic to use the signature, by ticking the corresponding checkbox in the HUB2 dashboard.
Once this option is enabled:
- Every request with
Environment: live sent to the endpoints listed above must include a valid X-Signature header.
- If the header is missing or the signature is invalid, the API returns an HTTP
400 error with the code signature_error.
As long as the option is disabled, live requests are not checked, even if they include the X-Signature header.
Before enabling this option in the live environment, make sure that signature generation is correctly implemented and thoroughly tested in the sandbox environment.
Keep your private key secure at all times, and update the public keys in the dashboard whenever you rotate your key pair.