Heatrail

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.

01

Choose

Pick the layer you already own.

Available

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.

Plugin documentation

Available

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.

SDK documentation

Available

REST API

HR1 HMAC authentication over JSON. POST /v1/check, POST /v1/labels, a strict payload allowlist and explicit error codes. For any language.

API reference

Figure 1

Three integration paths leave the shop as one request, under one budget.A large box on the left is labelled your shop, with a note that stage one HMAC runs here. Inside it three rows are stacked, labelled WooCommerce plugin, PHP SDK and REST API client. A short line leaves each row, the three merge into one, and a single arrow carries it to a box on the right labelled Heatrail, which answers with data, a score and reasons. The merged line is labelled POST slash v1 slash check, and below it, hashes and not contents. Beside it the figure reads 800 ms, the budget held in the shop. A closing line says that past the budget the client gives up, the order goes on without a signal, and the check is queued.Three paths, one request, one contractYour shopstage one HMAC runs hereWooCommerce pluginPHP SDKREST API clientPOST /v1/checkhashes, not contentsHeatrailanswers with datascore and reasons800 msbudget held in the shopPast the budget the client gives up, the order goes on without a signal, and the check is queued.
All three paths hash inside the shop and end at the same request, under the same 800 ms budget.

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.

02

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.
03

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 omitted
  • ip, the client IP your server observed (required)
  • order_ref and/or session_ref, at least one
  • persist_id, an optional returning-browser pseudonym
  • context, amount bucket, quantity, product risk class, account-age class, referral-source class
  • device_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 context and inside device_vector alike

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.

04

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 the Authorization header 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.

Start with the shortest path.

Heatrail

This site is not public yet

Heatrail is in a private pre-launch period. Enter the access password to continue.

Enquiries: hello@heatrail.eu