> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Location Verification API: Geofencing and IP Distance

> Confirm where a person is during verification: device position, geofencing, IP and document distance, spoofing checks. $0.05 per check.

Didit's Location verification step confirms where a person is while they verify, by reading the device position once (browser or native SDK) or the connection's IP location, and checking it against allowed and blocked areas, the IP location and the address on their documents.
In workflows and the API it is the `GEOLOCATION` feature, its settings are the `geolocation_*` keys, and its result is `geolocation_checks[]` on the session decision.

## When to use it

* You hold a licence for some jurisdictions only (gambling, brokerage, lending) and must prove the person was inside one when they applied.
* You must keep people out of blocked places, such as a sanctioned region or a state you do not serve.
* You want one more fraud signal: a device position far from the IP location or from the address on the ID, a mocked position, a VPN or an emulator.
* You do not need the person's movements or history, only one reading taken during the session.

## How it works

**1. Notice and permission**

The person sees why their location is needed and that it is read once, for this verification.
Their browser or phone then asks for permission.
Didit records the notice they accepted (version, language and time) as `consent` in the result.

**2. One position per attempt**

The client keeps the best reading until it is accurate enough or the per-attempt timeout (default 20 seconds) runs out.
Each attempt carries a single-use challenge that is valid for 5 minutes and bound to the session, so a captured submission cannot be replayed.

**3. Checks**

Didit judges accuracy, freshness, approximate grants, spoofing signals, areas, the distance to the IP location and to a document address, VPN, proxy, Tor and emulator findings, and the person's earlier checks.

**4. Outcome**

Each finding raises a [warning](https://docs.didit.me/core-technology/location-verification/warnings-location-verification.md) whose configured action (`NO_ACTION`, `REVIEW` or `DECLINE`) sets the step status.
The strongest action wins.

## Position sources

You choose where the position comes from with `geolocation_position_source`.

| Value | What happens |
| - | - |
| `device_and_ip` | The device position is checked against the IP location of the connection it was sent from. |
| `device_only` | The device position only. It is never compared with the IP location. |
| `ip_only` | The person is never asked for a permission. The connection's IP location (city level, 50 km radius) is judged instead, with no accuracy, freshness or approximate-grant rule. |
| not set | The device is asked, and the position is compared with the IP location the session's Device & IP Analysis step observed. A node that acts on a VPN, proxy or Tor finding looks the connection up and behaves like `device_and_ip`. |

On a node that asks the device, an attempt with no position is judged as the refusal or failure it is: the IP location never stands in for it.

## Configuration defaults and limits

All keys are optional.
The full list is in the [workflow feature configs reference](https://docs.didit.me/management-api/workflows/feature-configs.md#location-verification).

| Setting | Default | Limits |
| - | - | - |
| `geolocation_max_accuracy_radius_m` | 250 m | 10 to 50,000 m |
| `geolocation_max_age_seconds` | 120 s | 5 to 3,600 s |
| `geolocation_timeout_seconds` | 20 s | 5 to 120 s |
| `geolocation_max_attempts` | 3 | 1 to 5 |
| `geolocation_accuracy_mode` | `precise` | `precise` or `approximate_allowed` |
| `geolocation_low_accuracy_behavior` | `ask_again` | `ask_again` or `fail` |
| `geolocation_allow_qr_handoff` | on | on or off |
| `geolocation_allow_skip` | off | on or off |
| `geolocation_refusal_path` | `retry_then_handoff` | `retry_then_handoff`, `continue_without_location`, `end_session` |
| `is_geolocation_geofencing_enabled` | off | on or off |
| `geolocation_ip_distance_km` | 200 km | 0 or more, `null` turns the comparison off |
| `geolocation_document_distance_km` | off | 0 or more |
| `geolocation_travel_tolerance_enabled` | off | usual area 10 to 500 km (default 100), 1 to 90 days (default 14) |
| `geolocation_consecutive_mismatch_count` | off | 2 to 10 |

### Presets

`geolocation_preset` sets the default of every action you leave unset.
`licensing` declines a position outside the allowed areas, inside a blocked area, behind a VPN or Tor, or on an emulator, and reviews a proxy.
`fraud_screening` applies the same findings but sends everything `licensing` would decline to review.
An action you set explicitly always wins.

## Geofencing

Turn on `is_geolocation_geofencing_enabled` and list `geolocation_areas`.
An area is a whole country, regions (ISO 3166-2) of one country, a circle (1 to 500 km) or a polygon (3 to 200 points).
Only a position inside an allowed area passes, and a blocked area always wins over an allowed one.
A list holds up to 50 areas and needs at least one allowed area.

Didit judges the whole accuracy circle of the position, not just its centre:

* A circle fully outside every allowed area is `LOCATION_OUTSIDE_GEOFENCE`.
* A circle that crosses an edge that decides the answer is `LOCATION_GEOFENCE_UNDETERMINED`, never "outside".
* A circle fully inside a blocked area is `LOCATION_IN_BLOCKED_AREA`, and it is raised in addition to the allowed-area verdict.

A country or region ends where other land begins, not at its coastline.
The person is shown where the service is available, but never which areas are blocked.
Area rules also work on an `ip_only` step, judged from the IP location.
The older `geolocation_geofencing_by_country` setting still works but cannot be combined with `geolocation_areas`.

## Distance checks

| Check | Measured between | Setting |
| - | - | - |
| IP distance | The device position and the IP location, after subtracting both uncertainty radii (0 means they overlap). | `geolocation_ip_distance_km`, `geolocation_ip_distance_action` |
| Document distance | The position and an address, geocoded coarsely, so it is a weak signal. | `geolocation_document_distance_km`, `geolocation_document_distance_basis`, `geolocation_document_distance_action` |
| Streak | The person's earlier checks (same `vendor_data`) that were outside an allowed area, in a blocked area or too far from the address. | `geolocation_consecutive_mismatch_count` |

`geolocation_document_distance_basis` picks the address: `document` (the identity document, then the proof of address), `proof_of_address`, or `declared` (`expected_details.address` from session creation).
With `geolocation_travel_tolerance_enabled`, a person seen near their address in the last `geolocation_travel_max_days` days is not flagged for being far from it now.
Travel tolerance never excuses a VPN, a spoofed position, a blocked area or a position outside the allowed areas.

## Spoofing and integrity signals

* **Mock providers**: Android and iOS report when a position is simulated rather than measured.
* **Device attestation**: native SDKs can attach a device attestation. A failed attestation always declines. A missing one is recorded and defaults to no action. Browsers cannot attest and are recorded as `unsupported`, not as a failure.
* **VPN, proxy and Tor**: found on the connection that shared the location. When your action for one of them is not `NO_ACTION`, the person is asked to turn it off and check again while attempts remain.
* **Emulators**: reported by native apps only. An emulator ends the step on that attempt because no retry changes the device.
* **Replay**: a challenge presented on another session always declines.

## Outcomes

The accepted attempt of the step has one `outcome`:

| Outcome | Meaning |
| - | - |
| `verified` | A position that met every rule. |
| `low_accuracy` / `accuracy_undetermined` | The radius was wider than the limit, or the device reported none. |
| `stale` | The position was older than `geolocation_max_age_seconds`. |
| `approximate` | Only an approximate position was granted while a precise one was required. |
| `denied` / `restricted` | The person refused, or the system blocks location. |
| `unavailable` / `timeout` | The device found no position, or not in time. |
| `mock_detected` | The system reported the position as simulated. |
| `outside_geofence` / `in_blocked_area` | The position is outside every allowed area, or in a blocked one. |
| `anonymizer_detected` | A VPN, proxy, Tor exit or emulator was found and your workflow acts on it. |
| `skipped` | The person continued without sharing. |
| `replay_rejected` | The challenge belonged to another session. |
| `unsupported_client` | The app or SDK is too old to show the step. |

While attempts remain, a wide, stale, approximate, refused or area finding asks the person to try again, and only the last attempt raises warnings.
A mocked position, or an emulator your workflow acts on, ends the step on the attempt that reported it.

## Person experience

### Consent and permission

Nothing is read before the person accepts the notice and answers the system prompt.
The notice says the location is read once, for this verification, and is not a record of their movements.
A person who refuses is treated according to `geolocation_refusal_path`:

* `retry_then_handoff` asks again while attempts remain and then offers to continue on a phone.
* `continue_without_location` offers to go on without a location, and records the refusal as the outcome.
* `end_session` ends the step and the session on the first refusal. It needs `geolocation_permission_denied_action` to be `REVIEW` or `DECLINE`, and the session is never approved.

With `geolocation_allow_skip` on, the person can continue without a location at any time, and the step is judged by `geolocation_unavailable_action`.

### Browser

The hosted flow asks the browser for the position and shows step-by-step help for Chrome and Safari when location is off for the site.
A browser cannot tell a precise grant from an approximate one, so web results never raise `LOCATION_APPROXIMATE_ONLY`, and it never reports a mock provider, an emulator or an attestation.
A computer cannot share a precise position, so the flow offers a QR code that continues the same verification on the person's phone (`geolocation_allow_qr_handoff`, on by default), and the computer moves on by itself when the phone finishes.
When you embed the flow in an iframe, include `geolocation` in the `allow` attribute, see [in-context iframe](https://docs.didit.me/integration/web-sdks/incontext-iframe.md).
A page that blocks the geolocation permission policy is treated as the site's decision, not the person's refusal.

### Native SDKs

The iOS and Android SDKs ask the operating system and can attach device attestation and mock-location flags.
On iOS add `NSLocationWhenInUseUsageDescription` to `Info.plist`, and on Android the SDK declares `ACCESS_FINE_LOCATION` and `ACCESS_COARSE_LOCATION` and asks at runtime only when a workflow has this step.
See the [native SDKs](https://docs.didit.me/integration/native-sdks/overview.md).
An approximate grant on iOS or Android raises `LOCATION_APPROXIMATE_ONLY` unless `geolocation_accuracy_mode` is `approximate_allowed`.
An app or SDK build that predates the step is closed out with `unsupported_client` instead of leaving the session stuck.

## What the decision returns

The [session decision](https://docs.didit.me/sessions-api/retrieve-session.md) and the `status.updated` webhook carry two lists:

* `geolocation_checks[]`: one item per Location step, holding that step's accepted attempt, with its `warnings`.
* `geolocation_attempts[]`: every attempt of every Location step, oldest first.

See the [location verification report](https://docs.didit.me/core-technology/location-verification/report-location-verification.md) for every field.
The coordinates, accuracy, city and country are personal data of the end user, so a workflow's response attributes can restrict which `geolocation_checks` fields your decision returns.

## Pricing

Location verification costs **\$0.05 per check**, and it is not part of the free tier.
Didit charges once per Location step in a session, whatever its outcome, because the check ran and returned an answer.
Retries inside the same step are free.
A resubmission that completes the step again is charged again.

## Sandbox

Sandbox sessions run the same step in the same hosted flow, and the device or browser still reports a real position.
Sandbox sessions never reach the IP lookup service, so the IP comparison only uses what the session already holds.
Usage is recorded at a price of 0, so a sandbox session is never charged.
There are no magic inputs for this step.

## Related

* [Location verification report](https://docs.didit.me/core-technology/location-verification/report-location-verification.md) - every field of `geolocation_checks[]` and `geolocation_attempts[]`.
* [Location verification warnings](https://docs.didit.me/core-technology/location-verification/warnings-location-verification.md) - every `LOCATION_*` code and its action.
* [Workflow feature configs](https://docs.didit.me/management-api/workflows/feature-configs.md#location-verification) - every `geolocation_*` key.
* [Device & IP analysis](https://docs.didit.me/core-technology/ip-analysis/overview.md) - IP geolocation and VPN checks for the session.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.