Operations
createChallenge
Section titled “createChallenge”POST /v1/challenge
Request a challenge
The server determines the mode from the site configuration; the request has no mode field.
Authentication: none.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| variant 1 | object | |||
sitekey |
string | yes | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
hostname |
string | yes | minLength 1, maxLength 253, pattern ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$ |
Canonical hostname of the embedding page: equal to its own canonicalization (lowercase A-labels, no trailing dot, IPv4 in dotted-decimal form, valid punycode). |
action |
string | no | pattern ^[A-Za-z0-9/_]{1,100}$ |
|
cdata |
string | no | pattern ^[A-Za-z0-9_-]{0,255}$ |
|
sdk |
object | yes | ||
sdk.name |
string | yes | minLength 1, maxLength 40, pattern ^[a-z0-9@/._-]{1,40}$ |
SDK name: a-z 0-9 @ / . _ -, 1 to 40 characters. |
sdk.version |
string | yes | minLength 1, maxLength 40, pattern ^[0-9A-Za-z.+-]{1,40}$ |
SDK version: 0-9 A-Z a-z . + -, 1 to 40 characters. |
| variant 2 | object | |||
sitekey |
string | yes | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
app |
object | yes | The app: its platform and its package name or bundle id. | |
action |
string | no | pattern ^[A-Za-z0-9/_]{1,100}$ |
|
cdata |
string | no | pattern ^[A-Za-z0-9_-]{0,255}$ |
|
sdk |
object | yes | ||
sdk.name |
string | yes | minLength 1, maxLength 40, pattern ^[a-z0-9@/._-]{1,40}$ |
SDK name: a-z 0-9 @ / . _ -, 1 to 40 characters. |
sdk.version |
string | yes | minLength 1, maxLength 40, pattern ^[0-9A-Za-z.+-]{1,40}$ |
SDK version: 0-9 A-Z a-z . + -, 1 to 40 characters. |
Responses:
| Status | Description |
|---|---|
| 200 | Challenge issued. |
| 400 | Malformed or invalid request. |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type): a body whose media type is not application/json, or a non-identity Content-Encoding. |
| 500 | Internal error. |
solveChallenge
Section titled “solveChallenge”POST /v1/solve
Submit a challenge solution
A client that receives an unknown type MUST treat the solve as failed (not verified); it may retry or fall back.
Authentication: none.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
challenge |
string | yes | minLength 1, maxLength 4096 | Sealed, opaque challenge blob. Return it unchanged. |
pow |
object | yes | ||
pow.solutions |
array of integer | yes | minItems 1, maxItems 256 | |
signals |
string | yes | maxLength 49152 | The browser signals, encrypted for this challenge with the key from the challenge response’s signals bootstrap; opaque to every client but the widget. The edge scores them (protocol v1 section 6): an empty string (the widget could not collect or encode them) is scored as missing signals, and text that does not decrypt and validate (another challenge’s key, an unsupported collector release, garbage) as invalid signals. Both are scored, never refused: the solve still gets a token, carrying its lower score. |
attestation |
object | no | App solves only (M3): the attestation evidence for an app challenge, bound to it by the binding bytes of protocol v1 section 10. Absent on a web solve. |
Responses:
| Status | Description |
|---|---|
| 200 | A response token, or a puzzle to solve. |
| 400 | Malformed or invalid request. |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type): a body whose media type is not application/json, or a non-identity Content-Encoding. |
| 500 | Internal error. |
answerPuzzle
Section titled “answerPuzzle”POST /v1/puzzle/answer
Answer a puzzle
Checks the answer against the value sealed in the challenge. Each puzzle takes one answer (PUZZLE_MAX_ATTEMPTS) and its challenge string is then spent; the next response carries a fresh one. A person passes a group by answering PUZZLE_GROUP_CORRECT puzzles correctly (PUZZLE_GROUP_CORRECT_AUDIO in an audio group). A correct answer before that, or a wrong one within PUZZLE_GROUP_MAX_WRONG, returns the next puzzle of the group (step and steps count the progress); the answer that completes the group returns a token, and the wrong answer after PUZZLE_GROUP_MAX_WRONG wrong ones returns retry with a fresh challenge at PoW difficulty PUZZLE_POW_BUMP bits higher. A client that receives an unknown type MUST treat the answer as failed.
Authentication: none.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
challenge |
string | yes | minLength 1, maxLength 4096 | Sealed, opaque challenge blob. Return it unchanged. |
answer |
integer | yes | minimum 0, maximum 359 | The solver’s answer: slider position 0 to 100, rotation in degrees 0 to 359, or the index of the chosen option. |
telemetry |
string | yes | maxLength 8192 | Base64url of the interaction signals blob, sealed to the server with the same envelope as solve signals. A scoring input only; an empty string is scored as missing telemetry. |
Responses:
| Status | Description |
|---|---|
| 200 | A token, the next puzzle of the group, or a fresh challenge. |
| 400 | Malformed or invalid request. |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type): a body whose media type is not application/json, or a non-identity Content-Encoding. |
| 500 | Internal error. |
siteverify
Section titled “siteverify”POST /v1/siteverify
Verify a response token
Verifies a response token. Server-to-server only. Every input, authentication or authorization failure (missing or invalid secret; missing, invalid, expired or duplicate response; sitekey mismatch; disabled site; quota; a browser request carrying an Origin header) is answered with HTTP 200 and { "success": false, "error-codes": [...] }. A request carrying an Origin header gets error-codes: ["bad-request"] and no CORS headers. The only non-200 responses are 413 (body too large) and 5xx (internal error); clients MUST treat any non-200 as not verified. The secret is sent in the request body, or as Authorization: Bearer, never in the URL. A body in another media type, or with a non-identity Content-Encoding, is a malformed body: 200 with bad-request, never 415. Accepts application/x-www-form-urlencoded and application/json. (Server behaviour arrives in M1.) Firewall integrations send the secret as Authorization: Bearer rgsk_... instead of the body field secret; the secret is in exactly one of the two (both is bad-request). Unknown request fields are rejected (bad-request).
Authentication: none or secretKey.
Request body (application/x-www-form-urlencoded):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
secret |
string | yes | minLength 1, maxLength 128 | |
response |
string | yes | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
The response token: 1 to 2048 characters of unpadded base64url (A-Z a-z 0-9 - _). |
sitekey |
string | no | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
remoteip |
one of | no | End-user IP address: IPv4 (dotted decimal) or IPv6, including IPv4-mapped forms such as ::ffff:192.0.2.1. |
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
secret |
string | yes | minLength 1, maxLength 128 | |
response |
string | yes | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
The response token: 1 to 2048 characters of unpadded base64url (A-Z a-z 0-9 - _). |
sitekey |
string | no | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
remoteip |
one of | no | End-user IP address: IPv4 (dotted decimal) or IPv6, including IPv4-mapped forms such as ::ffff:192.0.2.1. |
Responses:
| Status | Description |
|---|---|
| 200 | Verification result. success: false with error-codes for every input, authentication or authorization failure. |
| 413 | Request body too large. |
| 500 | Internal error. |
createAssessment
Section titled “createAssessment”POST /v1/assessments
Create an assessment (token or express)
Server-to-server. A token assessment verifies a response token (one use, shared with siteverify) and returns its score and reasons; an express assessment (event.express: true) scores event.userIpAddress with no token. Body limit 8 KiB. JSON only; a query string or an Origin header is bad-request. event.userInfo is accepted as before and ignored when the site has neither account protection nor MFA on, and under ACCOUNT_KILL. Account verification (accountVerification) and the pair leak check (privatePasswordLeakVerification) are each off until the site enables them (403 feature-disabled). Payment fraud (event.transactionData, section 20) and SMS toll fraud (a phone number in event.userInfo, section 21) are each off until the site enables them (403 feature-disabled); event.fraudPrevention: DISABLED skips payment scoring. A card number, expiry, CVV or track data in transactionData is bad-request. Every other field not listed is rejected. The shadow score, the model, the fired rules and the rule, count or threshold behind a label are never returned.
Authentication: secretKey.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
event |
object | yes | ||
event.token |
string | no | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
|
event.siteKey |
string | no | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
event.expectedAction |
string | no | pattern ^[A-Za-z0-9/_]{1,100}$ |
|
event.express |
boolean | no | ||
event.userIpAddress |
string | no | maxLength 45 | End-user IP address (IPv4 or IPv6). Express only. |
event.userAgent |
string | no | maxLength 1000 | |
event.headers |
array of string | no | maxItems 50 | |
event.ja4 |
string | no | maxLength 64 | |
event.requestedUri |
string | no | maxLength 2048 | |
event.userInfo |
object | no | ||
event.userInfo.accountId |
string | no | minLength 1, maxLength 256 | Your account identifier: 1 to 256 UTF-8 bytes. Send this or hashedAccountId, never both. |
event.userInfo.hashedAccountId |
string | no | pattern ^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$ |
A hash of your account identifier you computed: canonical padded standard base64 of exactly 32 bytes (44 characters). Send this or accountId, never both. |
event.userInfo.createAccountTime |
string | no | pattern ^(?:(?:[0-9][0-9][2468][048]|[0-9][0-9][13579][26]|[0-9][0-9]0[48]|[02468][048]00|[13579][26]00)-02-29|[0-9]{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30)|(?:02)-(?:0[1-9]|1[0-9]|2[0-8])))T(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|([+-](?:[01][0-9]|2[0-3]):[0-5][0-9])))$ |
When the account was created (RFC 3339). At most 600 seconds before now makes the assessment an account creation; otherwise it is a login. |
event.userInfo.userIds |
array of object | no | maxItems 10 | Accepted for compatibility. Ignored, except that with the site’s smsTollFraud flag on the first phoneNumber is scored for SMS toll fraud (section 21): it is used only in request memory, stored only as keyed hashes and never logged. Nothing else here is stored, hashed or logged. |
event.userInfo.userIds[].email |
string | no | minLength 1, maxLength 320 | |
event.userInfo.userIds[].phoneNumber |
string | no | minLength 1, maxLength 32 | |
event.userInfo.userIds[].username |
string | no | minLength 1, maxLength 256 | |
event.fraudPrevention |
“FRAUD_PREVENTION_UNSPECIFIED” \ | “ENABLED” \ | “DISABLED” | no |
event.transactionData |
object | no | ||
event.transactionData.transactionId |
string | no | maxLength 128 | |
event.transactionData.paymentMethod |
string | no | pattern ^(credit-card|debit-card|gift-card|processor-[a-z0-9-]{1,32}|custom-[a-z0-9-]{1,32})$ |
|
event.transactionData.cardBin |
string | no | pattern ^([0-9]{6}|[0-9]{8})$ |
|
event.transactionData.cardLastFour |
string | no | pattern ^[0-9]{4}$ |
|
event.transactionData.currencyCode |
string | no | pattern ^[A-Z]{3}$ |
|
event.transactionData.value |
number | no | minimum 0, maximum 1000000000000 | |
event.transactionData.shippingValue |
number | no | minimum 0, maximum 1000000000000 | |
event.transactionData.shippingAddress |
object | no | ||
event.transactionData.shippingAddress.recipient |
string | no | maxLength 100 | |
event.transactionData.shippingAddress.address |
array of string | no | maxItems 4 | |
event.transactionData.shippingAddress.locality |
string | no | maxLength 100 | |
event.transactionData.shippingAddress.administrativeArea |
string | no | maxLength 100 | |
event.transactionData.shippingAddress.regionCode |
string | no | pattern ^[A-Z]{2}$ |
|
event.transactionData.shippingAddress.postalCode |
string | no | maxLength 16 | |
event.transactionData.billingAddress |
object | no | ||
event.transactionData.billingAddress.recipient |
string | no | maxLength 100 | |
event.transactionData.billingAddress.address |
array of string | no | maxItems 4 | |
event.transactionData.billingAddress.locality |
string | no | maxLength 100 | |
event.transactionData.billingAddress.administrativeArea |
string | no | maxLength 100 | |
event.transactionData.billingAddress.regionCode |
string | no | pattern ^[A-Z]{2}$ |
|
event.transactionData.billingAddress.postalCode |
string | no | maxLength 16 | |
event.transactionData.user |
object | no | ||
event.transactionData.user.accountId |
string | no | maxLength 256 | |
event.transactionData.user.creationMs |
string | no | pattern ^[0-9]{1,13}$ |
|
event.transactionData.user.email |
string | no | maxLength 320 | |
event.transactionData.user.emailVerified |
boolean | no | ||
event.transactionData.user.phoneNumber |
string | no | maxLength 32 | |
event.transactionData.user.phoneVerified |
boolean | no | ||
event.transactionData.merchants |
array of object | no | maxItems 5 | |
event.transactionData.merchants[].accountId |
string | no | maxLength 256 | |
event.transactionData.merchants[].creationMs |
string | no | pattern ^[0-9]{1,13}$ |
|
event.transactionData.merchants[].email |
string | no | maxLength 320 | |
event.transactionData.merchants[].emailVerified |
boolean | no | ||
event.transactionData.merchants[].phoneNumber |
string | no | maxLength 32 | |
event.transactionData.merchants[].phoneVerified |
boolean | no | ||
event.transactionData.items |
array of object | no | maxItems 20 | |
event.transactionData.items[].name |
string | no | maxLength 100 | |
event.transactionData.items[].value |
number | no | minimum 0, maximum 1000000000000 | |
event.transactionData.items[].quantity |
integer | no | minimum 0, maximum 1000000 | |
event.transactionData.items[].merchantAccountId |
string | no | maxLength 128 | |
event.transactionData.gatewayInfo |
object | no | ||
event.transactionData.gatewayInfo.name |
string | no | pattern ^[a-z0-9_-]{1,32}$ |
|
event.transactionData.gatewayInfo.gatewayResponseCode |
string | no | maxLength 64 | |
event.transactionData.gatewayInfo.avsResponseCode |
string | no | maxLength 8 | |
event.transactionData.gatewayInfo.cvvResponseCode |
string | no | maxLength 8 | |
accountVerification |
object | no | ||
accountVerification.endpoints |
array of one of | yes | minItems 1, maxItems 2 | |
accountVerification.languageCode |
string | no | maxLength 35, pattern ^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$ |
|
privatePasswordLeakVerification |
object | no | ||
privatePasswordLeakVerification.lookupHashPrefix |
string | yes | pattern ^[0-9a-f]{6}[048c]$ |
The bucket of the username: the first 26 bits of SHA-256 of the canonical username, 7 lowercase hex characters (bucketOf). |
privatePasswordLeakVerification.encryptedUserCredentialsHash |
string | yes | pattern ^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$ |
The blinded OPRF element of the canonical username and password: 32 bytes, unpadded base64url (43 characters). |
Responses:
| Status | Description |
|---|---|
| 200 | The assessment. |
| 400 | Malformed or invalid request (bad-request). |
| 401 | Missing, invalid or management secret, or a disabled site (unauthorized): one uniform answer. |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 403 | The site has not enabled this endpoint (feature-disabled). |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
| 503 | Assessments are switched off or unavailable (service-unavailable). Siteverify is unaffected. |
annotateAssessment
Section titled “annotateAssessment”POST /v1/assessments/{id}:annotate
Annotate an assessment
Labels an assessment of the authenticated site. A wrong site, a forged or altered id and a bad version are one 404 not-found; an id older than 30 days is 410 expired. Body limit 2 KiB. PASSWORD_* annotations are bad-request. transactionEvent reports the outcome of a scored payment; the reason PAYMENT_FRAUD is an alias the edge rewrites (section 20).
Authentication: secretKey.
Parameters:
| Name | In | Required | Type |
|---|---|---|---|
id |
path | yes | string |
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
annotation |
“LEGITIMATE” \ | “FRAUDULENT” | no | |
reasons |
array of “CHARGEBACK” \ | “CHARGEBACK_FRAUD” \ | “CHARGEBACK_DISPUTE” \ | “REFUND” \ |
accountId |
string | no | minLength 1, maxLength 256 | Your account identifier: 1 to 256 UTF-8 bytes. Send this or hashedAccountId, never both. |
hashedAccountId |
string | no | pattern ^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$ |
A hash of your account identifier you computed: canonical padded standard base64 of exactly 32 bytes (44 characters). Send this or accountId, never both. |
transactionEvent |
object | no | ||
transactionEvent.eventType |
“TRANSACTION_EVENT_TYPE_UNSPECIFIED” \ | “MERCHANT_APPROVE” \ | “MERCHANT_DENY” \ | “MANUAL_REVIEW” \ |
transactionEvent.reason |
string | no | maxLength 64 | |
transactionEvent.value |
number | no | minimum 0, maximum 1000000000000 | |
transactionEvent.eventTime |
string | no | pattern ^(?:(?:[0-9][0-9][2468][048]|[0-9][0-9][13579][26]|[0-9][0-9]0[48]|[02468][048]00|[13579][26]00)-02-29|[0-9]{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30)|(?:02)-(?:0[1-9]|1[0-9]|2[0-8])))T(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|([+-](?:[01][0-9]|2[0-3]):[0-5][0-9])))$ |
Responses:
| Status | Description |
|---|---|
| 200 | Recorded. Empty object. |
| 400 | Malformed or invalid request (bad-request). |
| 401 | Missing, invalid or management secret, or a disabled site (unauthorized): one uniform answer. |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 403 | The site has not enabled this endpoint (feature-disabled). |
| 404 | Unknown assessment (not-found). |
| 410 | Assessment older than 30 days (expired). |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
| 503 | Assessments are switched off or unavailable (service-unavailable). Siteverify is unaffected. |
checkPassword
Section titled “checkPassword”POST /v1/password-check
Check a password against the leaked-password ranges
Server-to-server. The body is the first 5 hex characters of the SHA-1 of the password; the answer is every leaked suffix with that prefix as SUFFIX35:COUNT lines (35 uppercase hex characters, a colon, a decimal count), CRLF separated, padded with fake count-0 lines to 800 to 1000 lines when shorter. The caller compares the suffix locally and ignores count 0. Cache-Control: private, no-store. Body limit 1 KiB. A prefix of any other length, a full hash or a non-hex character is bad-request. A missing range answers 503 service-unavailable, never an empty list.
Authentication: secretKey.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
prefix |
string | yes | pattern ^[0-9A-Fa-f]{5}$ |
The first 5 hexadecimal characters of the SHA-1 of the password (either case). |
Responses:
| Status | Description |
|---|---|
| 200 | The range, as text/plain lines SUFFIX35:COUNT. |
| 400 | Malformed or invalid request (bad-request). |
| 401 | Missing, invalid or management secret, or a disabled site (unauthorized): one uniform answer. |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 403 | The site has not enabled this endpoint (feature-disabled). |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
| 503 | Assessments are switched off or unavailable (service-unavailable). Siteverify is unaffected. |
eraseAccount
Section titled “eraseAccount”POST /v1/accounts:erase
Erase the stored profile of an account
Server-to-server. Deletes the profile and the related-account entries kept for the account of the authenticated site (GDPR erasure). Answers 200 whether or not anything was stored. Needs a valid site secret only: no feature flag, and neither ACCOUNT_KILL nor ASSESS_KILL stops it. Body limit 1 KiB. Exactly one of accountId and hashedAccountId.
Authentication: secretKey.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
accountId |
string | no | minLength 1, maxLength 256 | Your account identifier: 1 to 256 UTF-8 bytes. Send this or hashedAccountId, never both. |
hashedAccountId |
string | no | pattern ^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$ |
A hash of your account identifier you computed: canonical padded standard base64 of exactly 32 bytes (44 characters). Send this or accountId, never both. |
Responses:
| Status | Description |
|---|---|
| 200 | Done. Empty object. |
| 400 | Malformed or invalid request (bad-request). |
| 401 | Missing, invalid or management secret, or a disabled site (unauthorized): one uniform answer. |
| 403 | The site has not enabled this endpoint (feature-disabled). |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
| 503 | Assessments are switched off or unavailable (service-unavailable). Siteverify is unaffected. |
sendMfaCode
Section titled “sendMfaCode”POST /v1/mfa/send
Send the one-time code
Widget-facing, with the CORS and Origin rules of /v1/solve. Authenticated by the requestToken of an assessment response only. Body limit 1 KiB. Sends a six-digit code to the endpoint of the session: at most 3 sends per session, 60 seconds apart. A send that cannot happen answers 200 with a result instead of sent.
Authentication: none.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
requestToken |
string | yes | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
Responses:
| Status | Description |
|---|---|
| 200 | Sent, or a final result. |
| 400 | Malformed or invalid request (bad-request). |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 403 | The site has not enabled account verification (feature-disabled). |
| 404 | Forged, malformed or expired requestToken, or an unknown or disabled site (not-found): one answer in body and status. |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
| 503 | Account verification is switched off or unavailable (service-unavailable). Siteverify is unaffected. |
verifyMfaCode
Section titled “verifyMfaCode”POST /v1/mfa/verify
Verify the one-time code
Widget-facing, with the CORS and Origin rules of /v1/solve. Authenticated by the requestToken only. Body limit 1 KiB. A session allows 5 wrong codes in total. A wrong code with attempts left answers retry; the right code, and the last wrong one, answer a final token (whose assessment says SUCCESS_USER_VERIFIED or ERROR_USER_NOT_VERIFIED), given once per session. A session that is unknown or already over also answers retry.
Authentication: none.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
requestToken |
string | yes | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
|
code |
string | yes | pattern ^[0-9]{6}$ |
Responses:
| Status | Description |
|---|---|
| 200 | A final token, or retry. |
| 400 | Malformed or invalid request (bad-request). |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 403 | The site has not enabled account verification (feature-disabled). |
| 404 | Forged, malformed or expired requestToken, or an unknown or disabled site (not-found): one answer in body and status. |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
| 503 | Account verification is switched off or unavailable (service-unavailable). Siteverify is unaffected. |
getPolicyBundle
Section titled “getPolicyBundle”GET /v1/policies/{siteKey}
Fetch the signed firewall policy bundle of a site
Public by site key, unless the owner marked the bundle private: then Authorization: Bearer rgsk_... (the site secret) is required and a missing or wrong one is 401 unauthorized. Returns the bundle of section 18.5 as JSON with an ETag; a request with a matching If-None-Match gets 304. Cached for 60 seconds at the edge. The bundle is signed (Ed25519): integrations verify it against pinned keys and never trust the transport. An unknown or disabled site, and a site that has published no policy, are one 404 not-found. Integrations that hold a secret send it on every fetch (section 18.5).
Authentication: none or secretKey.
Parameters:
| Name | In | Required | Type |
|---|---|---|---|
siteKey |
path | yes | string |
Responses:
| Status | Description |
|---|---|
| 200 | The signed bundle. |
| 304 | Not modified. |
| 401 | A private bundle fetched without the matching site secret as Bearer (unauthorized). |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 404 | Unknown site or no published policy (not-found). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
getRgKeys
Section titled “getRgKeys”GET /.well-known/rg-keys.json
Public signing keys
The public halves of the bundle and session signing keys, with kid and notAfter, for monitoring and rotation. Integrations pin their keys (PINNED_POLICY_KEYS); this file never replaces the pins. (Server behaviour arrives in M9 Task 5.)
Authentication: none.
Responses:
| Status | Description |
|---|---|
| 200 | The keys. |
| 500 | Internal error. |
createSession
Section titled “createSession”POST /v1/session
Exchange a response token for a session or clearance token
Widget-facing, with the CORS and Origin rules of /v1/solve (the widget frame calls it). Spends the response token (one use) and returns an offline-verifiable token (section 18.6) that the page hands to the integration once, in the X-RG-Session-Init header (session) or the rg_clearance field of the /__rg/return form (clearance). The optional X-RG-Session-Prev header carries the previous session so its hard maximum age is kept. Body limit 1 KiB. 20 requests per second per site.
Authentication: none.
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
sitekey |
string | yes | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
token |
string | yes | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
A response token from /v1/solve: action rg_session for kind session, rg_challenge for kind clearance. It is spent here (one use). |
kind |
“session” \ | “clearance” | yes |
Responses:
| Status | Description |
|---|---|
| 200 | The token. |
| 400 | Malformed request, unknown or disabled site (bad-request, invalid-site-key, site-disabled), a hostname the site does not allow (hostname-not-allowed), or a response token that is invalid (MALFORMED), expired (EXPIRED), already spent (DUPE) or of the wrong action (UNEXPECTED_ACTION). |
| 402 | The organization has no active plan (plan-required), or its plan is restricted for this endpoint (plan-restricted). Section 19. |
| 403 | A refresh past the first token’s hard maximum age (session-expired). |
| 413 | Request body too large. |
| 415 | Unsupported media type (unsupported-media-type). |
| 429 | Rate limited (rate-limited). Carries Retry-After: 1. |
| 500 | Internal error. |
siteverifyCompat
Section titled “siteverifyCompat”POST /recaptcha/api/siteverify
Verify a response token (drop-in compatible path)
Verifies a response token. Server-to-server only. Every input, authentication or authorization failure (missing or invalid secret; missing, invalid, expired or duplicate response; sitekey mismatch; disabled site; quota; a browser request carrying an Origin header) is answered with HTTP 200 and { "success": false, "error-codes": [...] }. A request carrying an Origin header gets error-codes: ["bad-request"] and no CORS headers. The only non-200 responses are 413 (body too large) and 5xx (internal error); clients MUST treat any non-200 as not verified. The secret is sent in the request body, or as Authorization: Bearer, never in the URL. A body in another media type, or with a non-identity Content-Encoding, is a malformed body: 200 with bad-request, never 415. Accepts application/x-www-form-urlencoded and application/json. (Server behaviour arrives in M1.) Firewall integrations send the secret as Authorization: Bearer rgsk_... instead of the body field secret; the secret is in exactly one of the two (both is bad-request). Unknown request fields (for example version from existing server libraries) are ignored.
Authentication: none or secretKey.
Request body (application/x-www-form-urlencoded):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
secret |
string | yes | minLength 1, maxLength 128 | |
response |
string | yes | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
The response token: 1 to 2048 characters of unpadded base64url (A-Z a-z 0-9 - _). |
sitekey |
string | no | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
remoteip |
string | no | End-user IP address (IPv4 or IPv6). An invalid value is ignored, not rejected. |
Request body (application/json):
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
secret |
string | yes | minLength 1, maxLength 128 | |
response |
string | yes | minLength 1, maxLength 2048, pattern ^[A-Za-z0-9_-]{1,2048}$ |
The response token: 1 to 2048 characters of unpadded base64url (A-Z a-z 0-9 - _). |
sitekey |
string | no | pattern ^rgpk_[0-9A-Za-z]{20}$ |
Public site key. |
remoteip |
string | no | End-user IP address (IPv4 or IPv6). An invalid value is ignored, not rejected. |
Responses:
| Status | Description |
|---|---|
| 200 | Verification result. success: false with error-codes for every input, authentication or authorization failure. |
| 413 | Request body too large. |
| 500 | Internal error. |