On this page

Protected actions API

Redeem the request id a browser obtains for a protected action (login, signup, checkout) before your server lets the action proceed.

A protected action works in two steps. Your browser integration declares an action (for example login) and receives a requestId; your backend then redeems that id once with POST /v1/protected-actions/verify and decides whether the action may proceed. Use an sk_ server API key with the read:device scope, the default scope of every key, from your own server. Base URL: https://collect.noxtica.com.

EnvironmentBase URL
Productionhttps://collect.noxtica.com

Credential: ApiKeyAuth. See Authentication.

How this page is organisedBar length shows each section’s share of the text. Select a section to jump to it.

Protected Actions

1 operation

Operations in Protected actions API

Redeem a protected-action request id

POST /v1/protected-actions/verify

Call this from your backend before letting a protected action (login, sign-up, checkout, …) proceed. Send the requestId your browser integration obtained for the action: the resolved value of noxticaClient.protect(name), the hidden nox_request_id input of a data-nox-protect form, or form.dataset.noxRequestId / the noxtica:protected event of a script-submitted form. Use an sk_ key with the read:device scope, the default scope of every key.

Single use inside a 300-second window. The first valid call consumes the receipt and answers 200 with consumed: true. A replay inside the window is 409 already_consumed. An unknown, expired or other-organisation id is 404 request_not_found, and after 300 seconds every id is 404.

Binding. action is required: an id minted for login never satisfies a checkout check (422 action_mismatch; the receipt is not consumed). expectedDomain (a hostname, origin or URL) additionally binds the page that ran the action (422 domain_mismatch; not consumed). A request that arrives without a requestId (a fail-open timeout, a blocked script) means “no client signal”, never a pass.

Result. verdict is the risk band (minimal, low, medium, high, critical); score (higher is riskier) and confidence (0 to 1) are the values the browser received, and this server-to-server call always returns them. The request body is limited to 2048 bytes. A 500 carries no ok field and never consumes the receipt.

Authentication: Bearer token (ApiKeyAuth), scope read:device.

Request body

Content type application/json, required.

FieldTypeRequiredDescription
requestIdstring (uuid)YesThe protected submission’s request id.
actionstringYesThe protected action name the tenant expects (login, checkout, …). Pattern ^[a-z][a-z0-9_-]{0,31}$.
expectedDomainstringNoOptional hostname/origin/URL the action must have run on. At most 269 characters.

Responses

StatusMeaningError codesContent
200The receipt was valid and has now been consumed.application/json
400invalid_body, invalid_request_id, invalid_action or invalid_expected_domain.invalid_action, invalid_body, invalid_expected_domain, invalid_request_idapplication/json ProtectedActionVerifyError
401The Authorization: Bearer sk_... header is missing or malformed, or the key is unknown, revoked, or belongs to a suspended or deleted organisation. The body is deliberately identical for every cause.invalid_api_keyapplication/json InvalidApiKeyError
403The API key is valid but does not carry the scope this operation requires; error names the missing scope.API key does not have read:device scopeapplication/json ScopeForbiddenError
404request_not_found — unknown, expired (older than 300 s) or minted for another tenant.request_not_foundapplication/json ProtectedActionVerifyError
409already_consumed — the receipt was already redeemed (replay inside the window).already_consumedapplication/json ProtectedActionVerifyError
413payload_too_large — over the 2048-byte cap.payload_too_largeapplication/json ProtectedActionVerifyError
422action_mismatch or domain_mismatch — the receipt is NOT consumed.action_mismatch, domain_mismatchapplication/json ProtectedActionVerifyError
429Rate limit exceeded. Wait Retry-After seconds (also retryAfter in the body) before retrying; the response carries no other rate-limit headers.Rate limit exceededapplication/json
500Unexpected server fault. Retry with backoff and quote requestId to support if it persists.Internal server errorapplication/json ErrorEnvelope

Response headers

StatusHeaderTypeDescription
429Retry-AfterintegerSeconds until the bucket allows another request.

Response fields (200, application/json)

FieldTypeAlways returnedDescription
okbooleanYesAllowed values: true.
requestIdstring (uuid)Yes
actionstringYes
fingerprintIdstringYesThe device id of the protected submission (the same value the dashboard keys on).
verdictstringYesAllowed values: minimal, low, medium, high, critical.
scorenumberYes
confidencenumberYesMinimum 0, maximum 1.
domainstringYesHostname of the page that ran the action.
issuedAtstring (date-time)Yes
expiresAtstring (date-time)Yes
consumedbooleanYesThis call consumed the receipt. Allowed values: true.
consumedAtstring (date-time)Yes

Response example (200)

{
  "ok": true,
  "requestId": "8f1c2a52-6a6e-4b0e-9d52-2a7c1d9e4b10",
  "action": "login",
  "fingerprintId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "verdict": "low",
  "score": 12,
  "confidence": 0.91,
  "domain": "www.example.com",
  "issuedAt": "2026-10-08T10:00:00.000Z",
  "expiresAt": "2026-10-08T10:05:00.000Z",
  "consumed": true,
  "consumedAt": "2026-10-08T10:00:02.000Z"
}

Example request

curl

curl -X POST "https://collect.noxtica.com/v1/protected-actions/verify" \
  -H "Authorization: Bearer $NOXTICA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "requestId": "8f1c2a52-6a6e-4b0e-9d52-2a7c1d9e4b10",
  "action": "login",
  "expectedDomain": "www.example.com"
}'

Node.js (global fetch, Node 18 or later)

const response = await fetch('https://collect.noxtica.com/v1/protected-actions/verify', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NOXTICA_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    requestId: '8f1c2a52-6a6e-4b0e-9d52-2a7c1d9e4b10',
    action: 'login',
    expectedDomain: 'www.example.com',
  }),
});
console.log(response.status, await response.text());

Python (standard library)

import json
import os
import urllib.request

request = urllib.request.Request(
    "https://collect.noxtica.com/v1/protected-actions/verify",
    data=json.dumps({
        "requestId": "8f1c2a52-6a6e-4b0e-9d52-2a7c1d9e4b10",
        "action": "login",
        "expectedDomain": "www.example.com",
    }).encode(),
    headers={
        "Authorization": f"Bearer {os.environ['NOXTICA_API_KEY']}",
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request) as response:
    print(response.status, response.read().decode())

This page is generated from the Noxtica OpenAPI specification release v0.4.0: v1.yaml version 1.3.0-2026-10-27 (SHA-256 3fd15257a022). Do not edit it by hand.