Developers
Integration hub.
Three ways to send a check: the WooCommerce plugin, the dependency-free PHP SDK and the REST API. The path decides how much you write. It does not change the contract. The call is advisory, it is bounded at 800 ms, and contact details are hashed inside your own server before anything leaves it.
Choose
Pick the layer you already own.
WooCommerce plugin
A WordPress plugin for WooCommerce shops. Hashing, request signing, the timeout budget, the retry queue, the order-list badge and one-click labelling are handled for you. There is no code to write.
PHP SDK
Dependency-free, PHP 7.3 through 8.5, needing only ext-curl and ext-json. Fail-open and HMAC-signed by construction. For a shop on its own PHP checkout.
REST API
HR1 HMAC authentication over JSON. POST /v1/check, POST /v1/labels, a strict payload allowlist and explicit error codes. For any language.
Figure 1
Any platform that can make an outbound HTTPS request from the server integrates through the REST API, and that one request is the whole integration. Tell us what you run at hello@heatrail.eu.
Contract
Advisory, bounded, and safe to lose.
Heatrail is called once per order, from your server, at the point where you are about to hand the order to your payment provider. The response is data. It cannot block, delay or cancel anything, because the service has no mechanism to do so. The decision is yours, in your code, after the call returns.
The 800 ms budget
The plugin and the SDK enforce a hard 800 ms per-request timeout (HeatrailClient::DEFAULT_TIMEOUT_MS). Past it the client abandons the request, returns a degraded result, writes the check to a local retry queue and hands control straight back to your checkout. The budget runs in your process, not in ours, which is what makes it enforceable. Advisory by construction sets out the rest of that argument.
A degraded result is a fixed, recognisable shape, never an exception, never a partial score:
['degraded' => true, 'score' => 0, 'reasons' => []]Treat degraded as “no signal for this order”, never as “low risk”. The score is zero because there is nothing to report, not because the order was assessed and cleared.
What must be true in your code
- The call is not on the critical path
- Nothing about placing the order may depend on the response arriving. If you cannot delete the Heatrail call from your checkout and still ship the order, the integration is wrong.
- Every failure is caught
check()in the SDK never throws. Timeout, connection error, non-2xx and malformed JSON all return the degraded shape. If you call the REST API directly, you own that contract yourself: wrap it, bound it, swallow it.- Labelling is the exception
label()is the one operation that throws on failure. A label is an explicit human decision, so the SDK will never report a label as applied that the API did not confirm. Show the operator the failure and let them retry.- Scores are for routing, not for verdicts
- A score of 70 or more is what Heatrail treats as worth a merchant alert. What you do with it, hold for review, ask for a second factor, ship anyway, is a business decision that stays entirely on your side.
Data
Hash on your side. Send categories, not contents.
The e-mail address and the phone number are hashed inside your shop, before the request is built. Stage one is HMAC-SHA256(key = network salt, message = normalised value), producing 64 lowercase hex characters. That hex is what travels. Raw contact details never reach us.
Stage two runs in our server before anything is stored: argon2id with 12 MiB of memory per attempt, under a key that never leaves that server. Only the stage two value is kept, so the rows in our database are not even the values you computed.
The plugin and the SDK do this for you. On a direct REST integration it is your code, and it is the single most important line of the integration to get right.
What you send
email_h/phone_h, stage-one hashes, or omittedip, the client IP your server observed (required)order_refand/orsession_ref, at least onepersist_id, an optional returning-browser pseudonymcontext, amount bucket, quantity, product risk class, account-age class, referral-source classdevice_vector, optional coarse device characteristics
What is refused
- Raw e-mail addresses and phone numbers
- Names, delivery or billing addresses
- Card numbers or any payment credentials
- Order lines, product names, free text of any kind
- Raw user-agent strings
- Any key not on the allowlist, unknown fields are a 422, at the top level, inside
contextand insidedevice_vectoralike
The empty-hash rule
Never hash an empty, whitespace-only or placeholder value, and never substitute a fallback. A hash of a shared dummy would become one enormous node linking every guest checkout in the network to every other. When there is no usable value, the field is omitted from the payload entirely. The SDK signals this by returning null from hashEmail() and hashPhone(), and the API independently rejects the hashes of known placeholders with a placeholder_hash error.
Credentials
Three values, three different jobs.
- Key ID
- Public identifier of your API key, of the form
hk_followed by 16 hexadecimal characters. It appears in theAuthorizationheader and in our logs. It is not a secret. - API key secret
- Used for one thing only: computing the HR1 request signature. It never hashes an identifier, and it is never sent, only the signature derived from it is. Rotate it from the portal if it is ever exposed.
- Network salt
- Used for one thing only: stage-one identifier hashing. It is network-wide, which is what allows a hash computed in your shop to match the same person’s hash computed in another. Treat it with the same care as the API secret.
- Scopes
- A key carries
check,label, or both. A request signed with a key that lacks the required scope is refused with the same generic 401 as a bad signature, deliberately, so the response cannot confirm that a signature was otherwise valid.
All three values are server-side. None of them may ever be shipped to a browser, a build artefact or a client-side bundle. They are issued per shop from the partner portal, where the Terms of Service and the Data Processing Agreement are also accepted, once the application has been reviewed and a subscription is active. To ask a person first, write to hello@heatrail.eu.