In Ghana, "Adjacent Goil Filling Station, Madina Zongo Junction" is a perfectly normal address. Riders call customers three times to find the gate, cash on delivery goes unreconciled, and nobody can prove the parcel reached the right place.
I've been building AfriHex to fix that whole chain, and today the official TypeScript SDK is on npm. Here's a tour of what you can build with it.
npm install @afrihex/sdk It's typed end to end, has zero dependencies, and runs in Node.js 18+ and modern browsers.
1. Get a key, then a test key
Sign up for a free key with one request (no password, no card):
curl -X POST "https://api.afrihex.com/v2/self/signup" \ -H "Content-Type: application/json" \ -d '{"name": "Your App", "email": "you@example.com"}' Then create a test key so your experiments don't touch real customers:
import { AfriHex } from '@afrihex/sdk' const afrihex = new AfriHex({ apiKey: process.env.AFRIHEX_API_KEY! }) const { key } = await afrihex.account.createTestKey() // afx_test_…, shown once const test = new AfriHex({ apiKey: key }) Test calls don't use your quota, keep their data separate from live data, and never send real SMS.
2. Parse addresses the way people actually write them
parse understands "adjacent", "opposite", "behind", "beside", "near", "next to" and "after":
const { parsed } = await afrihex.addresses.parse( 'Adjacent Goil Filling Station, Madina Zongo Junction' ) parsed?.anchors?.[0] // { relation: 'adjacent', name: 'goil filling station' } parsed?.geocode_query // 'madina zongo junction' // Find the landmark itself, anchored to the place const goil = await afrihex.landmarks.geocode({ q: 'goil', anchor: 'Madina Zongo Junction', kind: 'fuel_station', }) goil.matches[0] // Goil, 276 m from the junction Reverse geocoding works the other way, describing a point by a landmark a rider will recognise:
const here = await afrihex.addresses.reverse(5.6319, -0.14, { descriptors: true }) here.descriptor?.text // "Opposite Nick Petroleum filling station, Gbatsuna" 3. Route a rider
const route = await afrihex.routing.route({ from: { point: { lat: 5.6457, lng: -0.075 } }, to: { gps_code: 'GA-183-8164' }, mode: 'motor_scooter', }) For a fleet, routing.plan assigns jobs across riders, including several trips per rider.
4. Track the delivery and reconcile the cash
const trip = await afrihex.trips.create({ destination: { gps_code: 'GA-183-8164' }, rider_ref: 'kofi', amount_to_collect: 120, recipient_phone: '0241234567', }) trip.tracking_url // send this to the customer await afrihex.trips.ping(trip.id, { lat: 5.64, lng: -0.10 }) await afrihex.trips.recordAttempt(trip.id, { reason: 'customer_unreachable', next_action: 'retry' }) await afrihex.trips.recordCollection(trip.id, { amount: 120, method: 'cash' }) await afrihex.trips.update(trip.id, { status: 'completed' }) const balances = await afrihex.cash.balances() // collected, handed in, still owed per rider You can also quote prices with delivery.quote and issue proof of delivery at the gate with pod.issue and pod.confirm.
5. Stable location codes, even offline
Every location gets an AfriHex code, built on Uber's open H3 grid:
const hex = await afrihex.hex.encode(5.6221843, -0.1729361, 7) hex.code // "AF-GH-7-0GXTJJBDZZZZZ" (store this) hex.short_code // "AF-GH-7-0GXTJJBD" (show this) Rider on a road with no signal? The companion package @afrihex/offline creates and reads codes on the device with no internet.
Errors are typed
import { AfriHexError } from '@afrihex/sdk' try { await afrihex.trips.get('unknown-id') } catch (err) { if (err instanceof AfriHexError) console.error(err.code, err.status) // TRIP_NOT_FOUND, 404 } A note on keys
Keep your API key on your server. For mobile apps, mint a per-user token instead of shipping your developer key in the app. The Mobile Integration guide shows the pattern.
Try it
- SDK guide: developer.afrihex.com/sdk
- Quickstart: developer.afrihex.com/quickstart
Trips, cash, fleet planning and pricing need a paid plan; addresses, routing and hex codes work on the free tier.
If you're building anything with deliveries or addresses in Ghana, I'd love to hear what you're working on. Drop a comment, or tell me which SDK you'd want next.