Skip to main content

Create Field-Service Zones with H3

The business problem​

"Where do we operate" sounds like one question, but AfriHex has three different tools for it, built for different situations, and picking the wrong one means either rebuilding coverage logic yourself or paying for recomputation you didn't need:

  • A raw H3 cell set you assemble and store yourself.
  • A service area — a named, standalone resource checked with one call.
  • A geofence — a monitoring-scoped shape that triggers webhook alerts.

What AfriHex returns​

ApproachEndpointShape inputWhat you get backUse it for
Raw cell setPOST /v2/hexcode/bulk + POST /v2/hexcode/compactA list of coordinates or hex codes you already haveA compacted list of hex codes you store and query yourselfFull manual control — custom coverage logic, your own storage, no per-check API call
Service areaPOST /v2/service-areacircle (center + radius_km), polygon (ring of points), or isochrone (center + drive-time minutes)A standalone resource with a server-computed H3 cell-cover (cell_count), checked via POST /v2/service-area/check"Can we deliver here?" — merchant coverage, independent of any customer or loan
GeofencePOST /v2/geofence/createSame three shapes (circle/polygon/isochrone)A shape scoped to consented monitoring, optionally to one customer_id/loan_id, that fires geofence_breach webhooks"Alert me when this specific customer enters/exits this area"

Service areas and geofences accept the same three shape types — the real decision isn't about geometry, it's whether you want a standalone, independently-checkable resource (service area) or a monitoring-scoped trigger tied to a specific customer (geofence). Don't build both for the same shape; pick based on which question you're actually asking.

Before you begin​

  • If you're going to check many points against the same shape repeatedly, use a service area or geofence — both are pre-computed as an H3 cell-cover server-side, so check is a cell lookup, not a live polygon point-in-shape test.
  • Geofences that scope to customer_id/loan_id require Banking Suite access for the wider monitoring flow they belong to — see Location Monitoring.
  • Decide your H3 resolution up front (resolution, 6–10, default 9 ≈ street-block granularity) — coarser resolutions mean fewer cells to store and faster checks, at the cost of a chunkier boundary. See Hex Addressing — cell size by resolution.

Copyable request​

A drive-time coverage area for a delivery merchant:

curl -X POST "https://api.afrihex.com/v2/service-area" \
-H "X-API-Key: $AFRIHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Accra Central — 30 min",
"type": "isochrone",
"center": { "lat": 5.56, "lng": -0.196 },
"minutes": 30,
"mode": "driving"
}'

Full example response​

{
"success": true,
"data": {
"id": "sa_21",
"name": "Accra Central — 30 min",
"type": "isochrone",
"resolution": 9,
"cell_count": 320,
"center": { "lat": 5.56, "lng": -0.196 },
"created_at": "2026-07-20T12:00:00Z"
}
}

Field-by-field explanation​

  • cell_count — how many H3 cells at resolution cover this shape. Useful as a sanity check (a "30-minute drive" zone with 20 cells is probably a bad request, not a small city) and as a rough cost signal — more cells means more storage, not more compute per check.
  • type echoed back — always the canonical name (isochrone, circle, polygon), even if you're used to seeing drive_time/radius in other tools' vocabulary.
  • resolution — fixed at creation time. Changing your mind later means creating a new service area, not patching the resolution on an existing one.

Error and edge cases​

  • A polygon with a self-intersecting or unclosed ring. Validate polygon rings client-side before sending — polygon must be a closed exterior ring of [lng, lat] pairs (note the coordinate order — longitude first).
  • An isochrone request with an unreachable mode/origin combination. A minutes contour computed from a point with no road access (an island, a restricted zone) can return an unexpectedly small or empty cover — sanity check cell_count rather than assuming the shape matches your mental model of "30 minutes."
  • Two overlapping service areas. POST /v2/service-area/check reports areas: [...] as an array for exactly this reason — a point can be inside more than one; don't assume in_area: true means exactly one match.
  • A geofence and a service area covering the same shape. They're independent resources with independent lifecycles — deleting one has no effect on the other, even if you created them from identical parameters.

Production checklist​

  • Pick service area vs. geofence based on the actual question ("can anyone deliver here" vs. "alert me about this specific customer"), not whichever you built first.
  • Resolution is chosen deliberately and recorded — don't let different zones in the same system silently use different default resolutions.
  • POST /v2/service-area/bulk-check (or the geofence equivalent) is used for batch checks instead of looping single checks.
  • Stale/unused service areas and geofences are deleted (DELETE /v2/service-area/{id}, DELETE /v2/geofence/{id}) — both are billed and counted resources, not free-standing config.