Anti-fraud
Anti-fraud is mandatory and it starts on your page. It is not a setting we switch on for you: it needs three things from your integration, and a flow that skips any of them is refused at the final step.
Read this before you build a flow, not after. It is the most common way a first integration fails, and the failure is misleading: a request whose page never loaded the script is refused with what looks like a fraud decline. Teams lose a day looking at the wrong end.
Your three obligations
1. Fetch the script from your backend, never from the browser.
GET /api/sp/v1/dcb/antifraud/script?shortCode=…&serviceId=…&ti=…&ts=…&te=…&flow=…
All six are required. flow is lp for the PIN flow on this page and he for
header enrichment; it tells the provider which journey it is
scoring, and the two are not interchangeable.
Your server calls us, we fetch the protection script using the platform's own anti-fraud account, and we return it for you to embed. You never hold anti-fraud credentials and never register with the provider.
If your web page calls this endpoint directly, your API token is published to every visitor. This endpoint looks like a front-end concern because its output is a script. It is not.
2. Load the script on the page: once, before your other scripts.
Embed it in <head>, ahead of anything else. Load it exactly once per page. Loading it twice
does not double the protection; it produces a page whose behaviour we cannot interpret.
3. Send the same ti and ts through the whole flow.
The identifiers you generate for the script call must be the ones you send on verification. They are required.
te: required here, whatever the provider's own docs say
te is the CSS selector of the element the subscriber actually clicks: your submit button, or the
form containing it.
The anti-fraud provider's public documentation lists te as optional. That is true only of a
different integration style, which this platform does not use. For this integration it is
mandatory: it is how the interaction is observed at all.
| Your page shape | What te should target |
|---|---|
| One page, updated in place (JavaScript swaps the view) | the confirmation button: the second click, not the first |
| Two pages (number, then code) | put the script on the code page, and target the form wrapping the input and its submit button |
Encoding te: pass the raw selector, unless you are writing the URL by hand
te is a query parameter, so the # in an id selector has to arrive as %23. Who does that
encoding is the part that catches people.
| How you build the request | What to pass |
|---|---|
An HTTP client or library (fetch, Guzzle, requests, Axios…) | the raw selector: #confirm-button |
| A URL you assemble as a string yourself, or a tool that substitutes variables literally | pre-encoded: %23confirm-button |
Passing %23 to a client that encodes for you sends %2523, and the platform then receives the
literal characters %23confirm-button as the selector. That matches no element on any page, so the
provider has nothing to observe. Nothing errors: the script call succeeds, the page looks healthy,
and verification is refused several steps later for a reason that points nowhere near this.
If you are unsure which kind your tool is, send one request and read the URL back from the
requestId's log entry with the operator team - it is cheaper than inferring it.
What expires, and when
| Lifetime | |
|---|---|
| The protection kit | 48 hours |
| A session token | 6 hours |
Fetch the script per page load rather than caching it. A cached script that has outlived its token produces a page that looks perfectly healthy and fails at verification.
What a refusal means
Two different failures produce two different codes, and telling them apart matters:
- Declined: the check ran and returned a negative verdict. The interaction was assessed. Do not retry; the answer will be the same.
- Unavailable: the check could not be completed. This is transient and retryable, and nothing was consumed by the attempt.
There is also a third case worth recognising: a transaction identifier we have no record of.
That means the script step did not happen for this ti: most often the page never fetched the
script, or fetched it with a different identifier. It has its own code, precisely so that it does
not get mistaken for a decline.
Why this is stricter for Header Enrichment
In a PIN flow, the subscriber also proves possession of the number by typing a code they received. In header enrichment there is no second factor: one tap is the entire consent. So the anti-fraud check is not one signal among several there; it is the only one. Expect no leniency.
Testing a blocked flow
Testing support for blocked-verdict flows is available on request through the operator team. Contact them and they will arrange it for your test service: you cannot self-serve it, and it does not require you to commit anything fraudulent.
Until the anti-fraud integration is switched on for an environment, every verdict there is allow, so a flow that passes in an environment without it is not evidence that it will pass with it.