On the Free plan the JWT carries no
risk_score claim at all — the key is omitted, not set to null. The category is still returned in the attestation response body as deviceIntelligence.riskCategory, but that value is not signed. There is no risk_category claim in the JWT on any plan. See JWT Claims for the full per-plan claim table.Score ranges
On Pro, Business, and Enterprise plans, the numeric score maps to these levels:Risk factors
The score considers multiple signals:- Jailbreak detection — Actual jailbreak/root signals. The strongest single factor.
- Development builds — Debug, Xcode, and TestFlight installs are scored as a separate, lower-weight signal than jailbreak. A dev build alone doesn’t mark a device as compromised — it’s expected during integration.
- Device integrity — Does the attestation pass Apple’s checks?
- Suspicious activity history — Past suspicious events raise the score, with recency decay: each clean week forgives one past event, so a device that stays clean earns its trust back over time.
- Device age — New devices (fewer than 3 attestations) carry a temporary penalty that clears as attestation history builds.
- Anomaly detection — Rapid country changes, app version downgrades, VPN/proxy networks, device model changes, and unusual timing patterns.
- Trust streak — A device with 30 consecutive clean attestations gets a credit against its score, and a larger one at 90.
Scores are dynamic in both directions: they rise on suspicious signals and fall again as a device builds a clean streak. Don’t persist a device’s score long-term — read it fresh from each attestation.
Using risk data
By category (all plans)
riskCategory is returned on every plan in the attestation response. It is an enum on the SDK side:
By numeric score (Pro / Business / Enterprise)
riskScore is Int? — it is nil on the Free plan, so unwrap it:
On your backend
Your backend reads the signed JWT, not the response body. The claims are flat andsnake_case, and there is no risk_category claim — derive it from risk_score:
risk_score is a snapshot from when the token was issued and tokens live for an hour by default. See JWT Claims for the full claim reference, the staleness caveat, and Node/Python verification code.
Webhooks
On Pro, Business, and Enterprise plans, webhooks fire for risk-related events:device.high_risk— A device’s score exceeds your thresholddevice.new— First attestation from a new devicedevice.attestation_failed— A device fails attestationattestation.anomaly— One or more anomalies are detected on an attestation
maxRiskScore, defaulting to 50 when unset. The payload carries both the score and the threshold that fired it. See Webhooks for setup and payload shapes.
Risk alerts
On Business and Enterprise plans you can define risk alert rules: a threshold, a comparison (gte or gt), and a webhook URL that Grantiva calls when a device’s score matches. Rules are independent of webhook endpoints and keep their own delivery log. Manage them from the risk alerts API. Reading the rules and delivery log also requires Business or above.