POST /v1/assessments — the request
Your backend requests an assessment with a single synchronous call to
POST https://api.fingertrackid.com/v1/assessments, signed with
HMAC.
The production API origin is https://api.fingertrackid.com; the dash also shows it, with a
copy button, as Endereço da API de risco in the Credencial servidor a servidor step of
Início. Configure it exactly as written, with no trailing slash or path. The path is exactly
/v1/assessments: no query string, no trailing slash and no normalization. The body is a single
JSON object in UTF-8.
POST /v1/assessments HTTP/1.1Host: api.fingertrackid.comContent-Type: application/jsonX-FTID-Timestamp: <second at which you sign this attempt>X-FTID-Request-Id: <a new lowercase UUIDv7>X-FTID-External-Event-Id: evt:tag-0001X-FTID-Signature: v1=<key_id>:<mac>
{"schema_version":"event-assessment-request.v1.1","external_event_id":"evt:tag-0001","idempotency_key":"idem-tag-0001","occurred_at":"2026-10-11T12:00:00.000Z","subject_ref":null,"account_ref":null,"event_context_token":null,"tag":"login","deadline_ms":null}Download the exact contract files of each profile. For v1.1: the
v1.1 OpenAPI contract,
request schema,
response schema and
synthetic examples. For
v1: the v1 OpenAPI contract,
request schema,
response schema and
synthetic examples. Both
profiles share the Problem schema,
and the v1.1 contract also references the v1 examples for two Problems.
Keep their relative directory layout when using the OpenAPI references. Their original example
server and implementation-status annotation are retained; ignore that example server and use
https://api.fingertrackid.com.
Production accepts both profiles: a correctly signed v1.1 body with your tag is answered with
event-assessment.v1.1, and the v1 request is unchanged.
Headers
Section titled “Headers”| Header | Value | Rule |
|---|---|---|
Content-Type |
application/json |
Exactly this. application/json; charset=utf-8 is rejected with 415 |
X-FTID-Timestamp |
UTC epoch in seconds, e.g. 1788523200 |
Digits only, no leading zero, at most 253402300799; accepted within ±300 s of the server clock |
X-FTID-Request-Id |
Canonical lowercase UUIDv7 | New for every send attempt; it is the anti-replay nonce |
X-FTID-External-Event-Id |
The body’s external_event_id |
Byte-for-byte equal to the one in the body and in the signed string |
X-FTID-Signature |
v1=<key_id>:<mac> |
A single signature; mac is 64 lowercase hexadecimal characters |
Do not send these headers:
| Header sent | Response |
|---|---|
Content-Encoding, any value, including identity |
415 unsupported-media-type |
Expect or Trailer; duplicated Host, Content-Length, Content-Type or Transfer-Encoding |
400 validation-failed |
X-Client-Id, X-Signature, X-Timestamp, X-Nonce |
401 authentication-failed |
X-FTID-Client-Id, X-FTID-Key-Id, X-FTID-Nonce, X-FTID-Event-Id, X-FTID-Business-Event-Id, X-FTID-Delivery-Id |
401 authentication-failed |
X-FTID-Assessment-Id on this route |
401 authentication-failed |
If your HTTP client adds Expect: 100-continue or ; charset=... on its own, turn that off. Do
not compress the request.
There is no X-Client-Id, no client_id in the body and no client-side choice of company,
application or environment: all of that is derived from the key_id once the signature checks
out.
The body has two profiles on this route, chosen by schema_version on every call:
v1.1, which names the moment with your own tag, and v1, with three fixed journeys
(journeys and tags). v1.1 is the recommended profile for new integrations and
is accepted in production; v1 remains supported, unchanged.
Every field is required, including the ones that accept null: omitting a field is not the
same as sending null. An unknown field is an error, and so is a field of the other profile.
| Field | Format | Rule |
|---|---|---|
schema_version |
"event-assessment-request.v1.1" or "event-assessment-request.v1" |
Selects the profile |
external_event_id |
^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$ |
ID of your business event; never reused |
idempotency_key |
8 to 255 visible ASCII characters (0x21–0x7E) |
Identifies the logical operation; never reused |
occurred_at |
RFC 3339 with seconds, explicit offset and up to 3 decimal places | When the event happened in your backend; a new operation must be within ±300 s of the server clock |
subject_ref |
Opaque reference (same grammar as external_event_id, at most 128 characters) or null |
Stable pseudonym of the person in your system (references) |
account_ref |
Opaque reference (same grammar as external_event_id, at most 128 characters) or null. Requires a non-null subject_ref, otherwise 400 validation-failed |
Stable pseudonym of the account. It stays bound to the subject_ref it was first sent with (references) |
event_context_token |
1 to 4096 visible ASCII characters, or null |
Web SDK token for this action; null = no browser context supplied |
tag |
v1.1 only. ^[a-z0-9][a-z0-9_]{0,63}$, with at most 10 digits in total |
Your name for the moment; never personal data (the tag) |
journey |
v1 only. LOGIN, WITHDRAWAL or TRADE |
The v1 profile |
action |
v1 only. Fixed per journey | The v1 profile |
journey_context |
v1 only. Closed object per journey | The v1 profile |
deadline_ms |
Integer within the range, or null |
v1.1: 250–5000 for every tag; v1: the journey’s range. null uses the default |
Person and account references
Section titled “Person and account references”subject_ref names the person and account_ref names an account of that person in your
system. A person may have several accounts; an account belongs to exactly one person.
- Build them as keyed pseudonyms, in your backend. For example, HMAC-SHA-256 over your
internal ID with a secret key that only your backend holds, written in hexadecimal after a
short prefix of your own (
usr_…,acc_…). A plain hash of a tax ID or an email is not a pseudonym: anyone can recompute it by guessing the input. Hexadecimal always fits the grammar. base64url needs a prefix, because it may start with-or_; standard base64 (+,/,=) is refused. The whole reference, prefix included, has at most 128 characters. - Keep the pair stable. Send the same pair for the same person and account on every call.
An
account_refstays bound to thesubject_refit was first sent with: the sameaccount_refwith anothersubject_refreturns409 binding-conflict, and no event or assessment is stored. Changing the HMAC key or the prefix creates new people and new accounts, so their history starts again. Never change the key ofsubject_refalone while keepingaccount_ref: every existing account would then return409. A new key forsubject_refalso makes each person a second, new person on the browsers they already used: for 30 daysDEVICE_MULTI_USERcounts both pseudonyms, so it overcounts the people on those devices (it isDETECTEDabove three people) until the old pseudonyms leave its window. - Send them whenever the user is identified. Send
subject_refon every assessed moment where your backend knows the user; for a login, after your backend has authenticated the user. Sendaccount_refwith it when the moment concerns one of that person’s accounts; it always travels with itssubject_ref. Each journey template says which references its moment takes: a password recovery, for example, sendsaccount_ref: null. - What they change. On the console,
account_reffeeds the account items of Contexto do acesso (access context), the accounts in Vínculos and the search by account. The 13 indicators andevidence_coverageare the same with or without it;trust_scoreand the recommendation change only when your company has activated a context rule on an account item (company risk rules).DEVICE_MULTI_USERcounts people throughsubject_ref, sosubject_refchanges the responses of later collections (why an indicator was not evaluated). - History starts at the first event that carries the reference. Earlier events are never linked to it afterwards. The first access of each account with a completed browser collection shows Primeira vez: ainda sem histórico (first time: no history yet) on the items that compare with the account’s past; without a completed browser collection they show Sem coleta concluída do navegador (no completed browser collection). A later login evaluates Dispositivo novo para esta conta only when an earlier access of the same account in the last 90 days also had a browser collection whose browser installation was linked; an account idle for more than 90 days shows Primeira vez again.
- One account per person. If your product has a single account per person, still send both, with a distinct, stable pseudonym for the account.
Body byte rules
Section titled “Body byte rules”- At most 16384 bytes, counted before any parsing or signature check. Above that:
413 payload-too-large. - UTF-8 without BOM, a single JSON document, nothing after it and no duplicate key at any level (including keys that only become equal after unescaping).
- Only objects, strings,
nulland integers; arrays and booleans are rejected. - Numbers must have an exact, safe integer value (up to 9007199254740991). Send plain integers.
- The signature and the server read the same bytes. Serialize once, with a single
JSON.stringifycall, keep the bytes, and sign and send exactly those bytes.
Request examples
Section titled “Request examples”A synthetic v1.1 example. Each journey template has its own.
{ "schema_version": "event-assessment-request.v1.1", "external_event_id": "evt_login_0001", "idempotency_key": "idem_evt_login_0001", "occurred_at": "2026-11-03T14:05:22.481Z", "subject_ref": "usr_5c0e", "account_ref": "acc_77d2", "event_context_token": "ect_login_0001", "tag": "login", "deadline_ms": null}Synthetic v1 examples, one per journey.
{ "schema_version": "event-assessment-request.v1", "external_event_id": "login_20260904_000001", "idempotency_key": "idem_login_20260904_000001", "occurred_at": "2026-09-04T12:00:00Z", "subject_ref": "subject_123", "account_ref": "account_456", "event_context_token": "ect_login_20260904_000001", "journey": "LOGIN", "action": "LOGIN_ATTEMPT", "journey_context": {}, "deadline_ms": null}{ "schema_version": "event-assessment-request.v1", "external_event_id": "withdrawal_20260904_000001", "idempotency_key": "idem_withdrawal_20260904_000001", "occurred_at": "2026-09-04T12:00:00Z", "subject_ref": "subject_123", "account_ref": "account_456", "event_context_token": "ect_withdrawal_20260904_000001", "journey": "WITHDRAWAL", "action": "WITHDRAWAL_REQUEST", "journey_context": { "amount_minor": 150000, "currency": "BRL", "beneficiary_ref": "beneficiary_789" }, "deadline_ms": 1000}{ "schema_version": "event-assessment-request.v1", "external_event_id": "trade_20260904_000001", "idempotency_key": "idem_trade_20260904_000001", "occurred_at": "2026-09-04T12:00:00Z", "subject_ref": "subject_123", "account_ref": "account_456", "event_context_token": "ect_trade_20260904_000001", "journey": "TRADE", "action": "TRADE_REQUEST", "journey_context": {}, "deadline_ms": 750}The examples are formatted for reading. In the real call, send the compact JSON that
JSON.stringify(object) produces.
Next step: HMAC authentication.