Gates
Management API
Note
Gates is available on all paid plans.
The Management API works with the same gates the dashboard edits. Use it to provision gates from code, keep rules in sync with configuration you store in your own repository, or pull decision logs into your own tooling.
These endpoints configure gates. To evaluate one, call the Decision Endpoint.
Authentication
All endpoints live under https://api.usercheck.com/v1/gates and expect your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
The key determines both the account and the environment you are working in. A key only sees gates in its own environment: a gate that belongs to another environment (or another account) returns 404 gate_not_found. To manage a different environment, use a key issued for that environment.
Management requests do not consume credits. Only the Decision Endpoint consumes credits.
Rate limits
Management requests are rate limited per second at the same per-plan rate as the rest of the API, but in their own bucket: a burst of management calls does not slow down your live decision or data API traffic, and the other way around. Exceeding the limit returns 429 rate_limit_exceeded with a Retry-After header.
Endpoints
| Method and path | Purpose |
|---|---|
GET /v1/gates |
List gates |
POST /v1/gates |
Create a gate |
GET /v1/gates/{gate_id} |
Get a gate |
PATCH /v1/gates/{gate_id} |
Update a gate |
DELETE /v1/gates/{gate_id} |
Delete a gate |
GET /v1/gates/{gate_id}/rules |
List rules |
POST /v1/gates/{gate_id}/rules |
Add a rule |
GET /v1/gates/{gate_id}/rules/{rule_id} |
Get a rule |
PUT /v1/gates/{gate_id}/rules/{rule_id} |
Replace a rule |
PATCH /v1/gates/{gate_id}/rules/{rule_id} |
Update part of a rule |
DELETE /v1/gates/{gate_id}/rules/{rule_id} |
Delete a rule |
POST /v1/gates/{gate_id}/rules/reorder |
Reorder rules |
GET /v1/gates/{gate_id}/decisions |
List decisions |
GET /v1/gates/{gate_id}/decisions/{decision_id} |
Get a decision |
A single gate, rule, or decision is returned as the object itself. Lists are wrapped in a top-level data array, and the paginated decision log adds links and meta next to it. Changes take effect immediately: the next decision request uses the rules as they exist at that moment.
The gate object
{
"id": "01k2wjgq8rnv6t0aefy3s7xdhb",
"name": "Signup Gate",
"environment": "production",
"default_action": "allow",
"rules_count": 2,
"created_at": "2026-08-12T09:15:27+00:00",
"updated_at": "2026-08-12T09:15:27+00:00"
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique identifier of the gate. Used in every URL below and in the Decision Endpoint URL. |
name |
string | Display name. |
environment |
string | The environment the gate belongs to. Always the key's environment. |
default_action |
enum | allow, block, or challenge. Applied when no rule matches. |
rules_count |
integer | Number of rules in the gate. |
List gates
GET /v1/gates
Returns every gate in the key's environment, newest first. The list is not paginated.
curl "https://api.usercheck.com/v1/gates" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "01k2wjgq8rnv6t0aefy3s7xdhb",
"name": "Signup Gate",
"environment": "production",
"default_action": "allow",
"rules_count": 2,
"created_at": "2026-08-12T09:15:27+00:00",
"updated_at": "2026-08-12T09:15:27+00:00"
}
]
}
Create a gate
POST /v1/gates
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Display name, up to 255 characters. |
default_action |
enum | Yes | allow, block, or challenge. Required so the fallback decision is explicit. |
environment |
string | No | Must match the key's environment. The gate is created in the key's environment either way, so you can omit it. |
curl -X POST "https://api.usercheck.com/v1/gates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Signup Gate", "default_action": "allow"}'
Returns 201 with the new gate.
A gate created through the API starts with no rules. This differs from the dashboard, where a new gate comes with a set of starter rules. Until you add a rule, every decision falls through to default_action.
Creating a gate fails with 409 gate_limit_reached once the environment holds 100 gates.
Get a gate
GET /v1/gates/{gate_id}
Returns the gate object. The rules themselves are served by the rules endpoints.
Update a gate
PATCH /v1/gates/{gate_id}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | New display name. |
default_action |
enum | No | allow, block, or challenge. |
Fields you omit keep their value. At least one writable field is required: a request naming none of them is rejected rather than answered with an unchanged gate. A gate's environment cannot be changed. Returns the updated gate.
Delete a gate
DELETE /v1/gates/{gate_id}
Deletes the gate and all of its rules. Returns 204 with no body. Decision requests for a deleted gate return 404 gate_not_found.
The rule object
{
"id": "01k2wjgqv9y1b7t3e8fs64dnkm",
"order": 1,
"name": "Block disposable emails",
"description": null,
"message": "Please use a permanent email address.",
"match": "all",
"action": "block",
"stop": true,
"enabled": true,
"conditions": [
{ "field": "email.disposable", "op": "eq", "value": true }
],
"created_at": "2026-08-12T09:16:03+00:00",
"updated_at": "2026-08-12T09:16:03+00:00"
}
| Field | Type | Writable | Description |
|---|---|---|---|
id |
string | No | Unique identifier of the rule. |
order |
integer | No | Position in the evaluation order. Changed with the reorder endpoint, not by editing the rule. |
name |
string | Yes | Short label, up to 255 characters. Shown in the dashboard and in decision logs. |
description |
string or null | Yes | Why the rule exists, up to 255 characters. |
message |
string or null | Yes | Up to 255 characters. Returned in the decision response when this rule sets the decision. |
match |
enum | Yes | all requires every condition to match (AND), any requires at least one (OR). |
action |
enum | Yes | allow, block, or challenge. |
stop |
boolean | Yes | When true, a match ends evaluation and this rule's action is final. Defaults to false. |
enabled |
boolean | Yes | When false, the rule is skipped. Defaults to true. |
conditions |
array | Yes | One or more conditions, each an object with field, op, and value. |
How rules combine into a decision, and the full list of condition fields, are covered in Rules & Conditions.
Condition operators
The operators a condition accepts depend on the field's type:
| Field type | Operators |
|---|---|
| boolean | eq, ne |
| number | eq, ne, lt, lte, gt, gte |
| string | eq, ne, contains, starts_with, ends_with, regex, not_regex, in, not_in, is_null, not_null |
The value follows the field and operator:
- Boolean fields take
trueorfalse; number fields take an integer. inandnot_intake an array of strings, for example{"field": "domain.tld", "op": "in", "value": ["xyz", "top"]}.regexandnot_regextake a bare PCRE pattern, such as^admin$. Do not wrap it in delimiters or append flags:/^admin$/and/foo/uare treated as pattern text, not as delimited expressions. Matching is case-insensitive by default (i); Unicode mode (u) is not enabled. PCRE inline options inside the pattern, such as(?s)or(?-i), are supported. By default,.and its quantifiers count bytes, not Unicode code points:^.{21,}$matches a string of at least 21 bytes, so it already matches 11 Cyrillic letters encoded as 22 UTF-8 bytes.is_nullandnot_nullneed novalue.- String comparisons are case-insensitive by default.
List rules
GET /v1/gates/{gate_id}/rules
Returns every rule of the gate in evaluation order. The list is not paginated.
Add a rule
POST /v1/gates/{gate_id}/rules
The body takes the writable fields of the rule object: name, match, action, and conditions are required, the rest are optional. The new rule is appended after the gate's last rule.
curl -X POST "https://api.usercheck.com/v1/gates/01k2wjgq8rnv6t0aefy3s7xdhb/rules" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Block disposable emails",
"message": "Please use a permanent email address.",
"match": "all",
"action": "block",
"stop": true,
"conditions": [
{ "field": "email.disposable", "op": "eq", "value": true }
]
}'
Returns 201 with the new rule, including its assigned order.
Adding a rule fails with 409 rule_limit_reached once the gate holds 200 rules.
Get a rule
GET /v1/gates/{gate_id}/rules/{rule_id}
Returns the rule object.
Replace a rule
PUT /v1/gates/{gate_id}/rules/{rule_id}
Replaces the whole rule. name, match, action, and conditions are required. Fields you omit are reset to their defaults: description and message to null, stop to false, enabled to true. The rule's order is not affected.
Update part of a rule
PATCH /v1/gates/{gate_id}/rules/{rule_id}
Changes only the fields you send; everything else keeps its value. At least one writable field is required. conditions is replaced as a whole when present: there is no way to edit a single condition in place.
curl -X PATCH "https://api.usercheck.com/v1/gates/01k2wjgq8rnv6t0aefy3s7xdhb/rules/01k2wjgqv9y1b7t3e8fs64dnkm" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
Delete a rule
DELETE /v1/gates/{gate_id}/rules/{rule_id}
Deletes the rule. Returns 204 with no body. The remaining rules keep their relative order. Decisions the rule made stay in the decision log and keep reporting its id and name.
Reorder rules
POST /v1/gates/{gate_id}/rules/reorder
Sets the evaluation order of the whole gate in one call. rule_ids must contain the id of every rule in the gate exactly once, in the desired order:
curl -X POST "https://api.usercheck.com/v1/gates/01k2wjgq8rnv6t0aefy3s7xdhb/rules/reorder" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rule_ids": [
"01k2wjgqvatb5x0hry2c9pmesw",
"01k2wjgqv9y1b7t3e8fs64dnkm"
]
}'
Returns the gate's rules in their new order. A list that misses a rule, repeats one, or includes a rule from another gate fails with 422 and no order is changed.
List decisions
GET /v1/gates/{gate_id}/decisions
Returns the gate's decision log, newest first, with cursor pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
period |
enum | No | Lookback window: 24h, 7d, or 30d (default). |
per_page |
integer | No | Results per page, 1 to 100. Defaults to 25. |
cursor |
string | No | Opaque cursor from a previous response. Omit for the first page. |
Filters
Every filter is a plain query parameter matching one exact value. Filters on different fields combine with AND:
| Parameter | Value |
|---|---|
action |
allow, block, or challenge |
rule_id |
A rule id of the gate, or default for decisions that fell through to the gate's default action. Ids of deleted rules stay valid, since their decisions outlive them. |
domain |
A domain. Normalized before matching, so case and format variants find the same rows. |
email |
A complete email address. |
ip |
An IP address. |
anonymity |
vpn, proxy, tor, or relay: decisions whose IP had that anonymity type detected. |
country |
A two-letter country code. |
The parameters are validated strictly: a value that cannot match anything (a malformed email, an unknown rule id, a made-up anonymity type) fails with 422, and so does a query parameter the endpoint does not know. A misspelled filter can therefore never answer 200 with an unfiltered list.
curl "https://api.usercheck.com/v1/gates/01k2wjgq8rnv6t0aefy3s7xdhb/decisions?period=7d&action=block&country=RU" \
-H "Authorization: Bearer YOUR_API_KEY"
Response
{
"data": [
{
"id": "01K2WJH8Y4VD0BS2R8Q1KTHVGR",
"action": "block",
"rule": {
"id": "01k2wjgqv9y1b7t3e8fs64dnkm",
"name": "Block disposable emails"
},
"domain": "mailinator.com",
"email": "[email protected]",
"ip": "1.1.1.1",
"country": "HK",
"anonymity": [],
"created_at": "2026-08-18T06:24:09+00:00"
}
],
"links": {
"next": null
},
"meta": {
"period": "30d",
"per_page": 25,
"next_cursor": null
}
}
List entries are summaries. rule is the rule that decided the entry, or null when the decision fell through to the gate's default_action. country is the two-letter country code of the evaluated IP, and anonymity lists what was detected on it, from vpn, proxy, tor, and relay. The full input and evaluated signals are returned by Get a decision.
To fetch the next page, pass meta.next_cursor as the cursor parameter, or follow links.next. A next_cursor of null means there are no further pages. A malformed cursor fails with 422 invalid_cursor.
Get a decision
GET /v1/gates/{gate_id}/decisions/{decision_id}
Returns one decision in full, in the same shape the Decision Endpoint returned when the decision was made: your application can parse one schema whether it is handling a live decision or reading one back. The decision id is the id field of that response.
{
"id": "01K2WJH8Y4VD0BS2R8Q1KTHVGR",
"gate_id": "01k2wjgq8rnv6t0aefy3s7xdhb",
"action": "block",
"rule": {
"id": "01k2wjgqv9y1b7t3e8fs64dnkm",
"name": "Block disposable emails",
"message": "Please use a permanent email address."
},
"input": {
"email": "[email protected]",
"ip": "1.1.1.1"
},
"signals": {
"email": { ... },
"domain": { ... },
"ip": { ... }
},
"duration_ms": 52.62,
"created_at": "2026-08-18T06:24:09+00:00"
}
rule.name and rule.message are the wording captured when the decision was made; editing a rule later does not rewrite them. input is the request body the decision was made for, and signals holds the email, domain, and IP signal objects the rules ran against.
Decisions are written to the log asynchronously, moments after the decision response returns. Fetching a decision immediately after receiving it can return 404 decision_not_found for that short window; retrying resolves it.
Errors
Errors return the matching HTTP status code and a JSON body with a stable error code and a human-readable message:
{
"error": "gate_not_found",
"message": "Gate not found."
}
Codes specific to these endpoints:
404 gate_not_found: Unknowngate_id, or a gate outside the key's environment404 rule_not_found: Unknownrule_id, or a rule belonging to a different gate404 decision_not_found: Unknowndecision_id409 gate_limit_reached: The environment already holds 100 gates409 rule_limit_reached: The gate already holds 200 rules422 invalid_cursor: Thecursorparameter is not a cursor from a previous response
Invalid input, including an unknown or malformed query parameter, fails with 422 validation_failed and an errors object naming the fields at fault:
{
"error": "validation_failed",
"message": "Some input fields are invalid.",
"errors": {
"name": ["The name field is required."]
}
}
On top of those, these endpoints return the codes shared by every Gates endpoint, including 401 unauthorized, 403 pro_only, and 429 rate_limit_exceeded. See Errors for the full list.