Policies
Ordered rules that match on an endpoint, the four actions, rate limits that survive IP rotation, and the kill switch.
A policy says what a verdict is worth on which endpoints. Policies are ordered and the first match wins, so put your specific rules above your general ones.
Actions
| Action | Effect |
|---|---|
allow | Let it through, and stop evaluating. |
log | Let it through, but record the decision. |
challenge | Send it through a step-up you define rather than refusing outright. |
block | Refuse it. |
In monitor mode challenge and block are recorded but not applied.
Conditions
A condition says when the rule fires, not which requests it approves of. Read every threshold
below as the bar a request must clear: the rule fires on requests that fall short of it. A rule
with "action": "block" and "min_verdict": "pass" therefore blocks degraded and failing
devices and lets passing ones through.
platforms is the exception. It narrows which requests the rule looks at rather than giving a
reason to act, so a request from a platform the rule does not list is simply not its business.
| Condition | The rule fires when |
|---|---|
min_verdict | The device verdict is worse than this. pass fires on degraded and fail; degraded fires on fail only. |
min_app_version | The app version is older than this. Compared number by number, so 2.10.0 is newer than 2.9.0, and a shorter version is padded, so 2.1 equals 2.1.0. |
per_device_per_minute | This Vouch device sent more requests in the last minute. |
per_ip_per_minute | This address sent more requests in the last minute. |
platforms | Never on its own. It restricts the rule to ios, android, or both; omit it to cover every platform. |
Conditions are alternatives, not a checklist. A rule with both min_verdict and
min_app_version fires when either falls short, and the first one that does is the reason
recorded against the decision. A rule with no conditions at all fires on every request its
pattern selects, which is how a blanket rule is written.
curl -X POST https://api.vouch.dev/v1/apps/app_123/security/policies \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"pattern": "/v1/sdk/*",
"action": "block",
"conditions": { "min_verdict": "pass" },
"enabled": true
}'Rate limits that cannot be evaded by changing address
Per-device limits key on the device identifier the SDK holds, not the IP address. A script rotating through a pool of addresses still counts as one device, which is the point: IP-based limits are trivially evaded by exactly the traffic you are trying to stop.
Keep a per-IP limit as well for traffic that has no device identifier at all.
Ordering
Reorder in one call:
curl -X PUT https://api.vouch.dev/v1/apps/app_123/security/policies/order \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{"ids":["pol_specific","pol_general"]}'The projection reports which rule would refuse each request, labelled with its pattern, so you can correct the order before enforcing rather than after.
The kill switch
Above every policy sits a switch for when something is happening now.
curl -X PUT https://api.vouch.dev/v1/apps/app_123/security/kill-switch \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{"device_id":"dev_abc","note":"Scripted abuse from this handset"}'It can block one device, one app version, or every unattested request. Blocking a device also revokes its live run tokens, so it loses access immediately rather than when its token expires.
Blocking an app version blocks everyone on it, including customers who have not updated. Check the projection for the device count on that version first.
Settings
| Setting | Default | Controls |
|---|---|---|
mode | monitor | Whether decisions are applied or only recorded. |
run_token_ttl_seconds | 900 | Run token lifetime. Never more than 86400. |
skew_seconds | 300 | How far a request timestamp may be from ours. |
verdict_cache_seconds | 3600 | How long a verdict is reused before re-attesting. |
require_attestation | off | Whether an unattested request is refused outright. |