BedKoi API for ambulance companies' software
Connected to BedKoi, your company's own dispatch software can take requests, report each step of a trip and say which vehicles are free by itself. The panel's rules apply. This guide is for the software's developers.
This guide as a PDF:বাংলা (PDF)English (PDF)
Getting started
- The company's admin signs in to the BedKoi panel and makes an API key on the company's “Connect software” page. The key is shown once; keep it secret. A hospital's key for its beds doesn't work here.
- Send the key with every request, in the X-Api-Key header. Every address starts with:
https://api.bedkoi.com/v1 - Keep saying which vehicles are free: requests come only for vehicles marked free.
curl https://api.bedkoi.com/v1/partner/ambulance/requests \
-H "X-Api-Key: bk_live_…"Endpoints
GETYour company: name, status, the districts it serves/ partner/ ambulance/ provider GETRequests offered to you now (offers), oldest first; and your trips under way (trips)/ partner/ ambulance/ requests POSTTake it: etaMinutes (minutes to the pickup), and vehicleId and driverId if you know them/ partner/ ambulance/ requests/ {tripId}/ accept POSTPass on it/ partner/ ambulance/ requests/ {tripId}/ decline GETYour trips (status=active or done, page, pageSize)/ partner/ ambulance/ trips GETOne trip: patient, the family's phone, destination, steps, fare/ partner/ ambulance/ trips/ {tripId} POSTSet or swap the vehicle and driver (vehicleId, driverId); a new driver is texted the trip's link/ partner/ ambulance/ trips/ {tripId}/ assign POSTThe next step, in order: ON_WAY (left), ARRIVED (at the pickup), PICKED_UP (patient in), DROPPED (at the destination)/ partner/ ambulance/ trips/ {tripId}/ step POSTWhere the vehicle is (lat, lng, from your GPS), until the patient is dropped; the family sees it on their page/ partner/ ambulance/ trips/ {tripId}/ location POSTYou can't go after all (until the patient is in): the request goes to the others again (reason)/ partner/ ambulance/ trips/ {tripId}/ give-back POSTThe final fare from your fare table: km, waitMinutes, oxygenHours, bearer, tollTaka; well above the estimate it needs a reason/ partner/ ambulance/ trips/ {tripId}/ fare POSTThe family paid the driver in cash: the trip is over/ partner/ ambulance/ trips/ {tripId}/ paid-cash GETYour vehicles, each with its state and how long it counts as free (freeUntil)/ partner/ ambulance/ vehicles PUTFree or off: { "available": true }/ partner/ ambulance/ vehicles/ {vehicleId}/ available GETYour drivers/ partner/ ambulance/ drivers
Taking a request
A new request comes as news (ambulance.offer), or check requests. A few nearby companies get it at once; the first to take it gets it, and a late answer gets 409 already_taken. The family's phone number is shown only once you've taken it.
curl -X POST https://api.bedkoi.com/v1/partner/ambulance/requests/<tripId>/accept \
-H "X-Api-Key: bk_live_…" \
-H "Content-Type: application/json" \
-d '{ "etaMinutes": 12, "vehicleId": "01a2…", "driverId": "01a3…" }'Running a trip
Set the vehicle and driver, then send the steps in order; the family is texted when you leave and arrive. If the vehicle has GPS, keep sending where it is. The driver can also take steps from their link; your software still hears of it (viaPartner: false). Send the fare at the destination; paid-cash once it's paid in cash.
curl -X POST https://api.bedkoi.com/v1/partner/ambulance/trips/<tripId>/step \
-H "X-Api-Key: bk_live_…" \
-H "Content-Type: application/json" \
-d '{ "step": "ON_WAY" }'
curl -X POST https://api.bedkoi.com/v1/partner/ambulance/trips/<tripId>/location \
-H "X-Api-Key: bk_live_…" \
-H "Content-Type: application/json" \
-d '{ "lat": 23.7461, "lng": 90.3742 }'
curl -X POST https://api.bedkoi.com/v1/partner/ambulance/trips/<tripId>/fare \
-H "X-Api-Key: bk_live_…" \
-H "Content-Type: application/json" \
-d '{ "km": 6.4, "waitMinutes": 15 }'Free vehicles
Requests come only for vehicles marked free, and “free” wears off after a while (freeUntil). While a vehicle is free, say so again now and then; a vehicle on a trip is left out by itself.
curl -X PUT https://api.bedkoi.com/v1/partner/ambulance/vehicles/<vehicleId>/available \
-H "X-Api-Key: bk_live_…" \
-H "Content-Type: application/json" \
-d '{ "available": true }'Request news (webhooks)
Give an https address on the “Connect software” page in the panel. BedKoi POSTs each message there, signed, and tries again a few times if it gets no answer. Every message has tripId and reference; viaPartner says whether your software did it.
Events
ambulance.offerA request is offered to you (details in offer); first to take it gets itambulance.offer_closedAn offer you didn't answer is gone: reason is taken (another company took it), cancelled or expiredambulance.trip_updatedA trip of yours changed (the whole trip in trip). change: accepted, assigned, step, fare, fare_answer (the family accepted the fare or said it isn't right), paid, fare_decided (BedKoi settled a complaint)ambulance.trip_cancelledThe family or BedKoi called off a trip of yours; no need to send the vehicleambulance.trip_given_backThe trip was given back (from your panel or software); it's no longer yoursping“Send a test” in the panel
POST https://dispatch.example.com/bedkoi
X-BedKoi-Event: ambulance.offer
X-BedKoi-Delivery: 01a1…
X-BedKoi-Timestamp: 1761552000
X-BedKoi-Signature: v1=5d41…
{
"event": "ambulance.offer",
"occurredAt": "2026-10-27T08:00:00.000Z",
"tripId": "01a1…",
"reference": "AM7K3Q9",
"viaPartner": false,
"offer": {
"tripId": "01a1…",
"reference": "AM7K3Q9",
"type": { "id": 2, "key": "ac", "nameEn": "AC ambulance", "nameBn": "এসি অ্যাম্বুলেন্স", "critical": false, "forDeceased": false },
"scheduledFor": null,
"pickup": { "address": "Road 12, Dhanmondi", "lat": 23.746, "lng": 90.376, "area": "ধানমন্ডি" },
"drop": { "address": "…", "lat": 23.738, "lng": 90.396, "area": "শাহবাগ", "hospital": { "nameEn": "…", "nameBn": "…" } },
"estimateKm": 4.2,
"estimateMinTaka": 1800,
"estimateMaxTaka": 2100,
"condition": "শ্বাসকষ্ট",
"floor": 3,
"hasLift": false,
"needsBearer": true,
"distanceKm": 1.8,
"round": 0,
"sentAt": "2026-10-27T08:00:00.000Z",
"createdAt": "2026-10-27T08:00:00.000Z"
}
}{
"event": "ambulance.trip_updated",
"occurredAt": "2026-10-27T08:14:00.000Z",
"tripId": "01a1…",
"reference": "AM7K3Q9",
"viaPartner": false,
"change": "step",
"trip": { "id": "01a1…", "status": "ARRIVED", "phone": "+8801…", "patientName": "…", "vehicleId": "01a2…", "driverId": "01a3…", "fare": { … } }
}Check the signature
X-BedKoi-Signature is v1= followed by HMAC-SHA256(secret, timestamp + "." + body), with the timestamp in X-BedKoi-Timestamp. Drop a message whose signature doesn't match or that is more than 5 minutes old. The same message can arrive twice: X-BedKoi-Delivery tells them apart.
// Node.js
import { createHmac, timingSafeEqual } from "node:crypto"
function fromBedKoi(rawBody, headers, secret) {
const ts = headers["x-bedkoi-timestamp"]
const sent = headers["x-bedkoi-signature"] ?? ""
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false
const expected =
"v1=" + createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex")
return (
sent.length === expected.length &&
timingSafeEqual(Buffer.from(sent), Buffer.from(expected))
)
}<?php
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_BEDKOI_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_BEDKOI_SIGNATURE'] ?? '';
$expected = 'v1=' . hash_hmac('sha256', $ts . '.' . $raw, getenv('BEDKOI_WEBHOOK_SECRET'));
if (abs(time() - (int) $ts) > 300 || !hash_equals($expected, $sig)) {
http_response_code(401);
exit;
}
$event = json_decode($raw, true);
http_response_code(200);Errors
400What you sent isn't right; the message says why.401No key, or a wrong or revoked one (api_key_missing, api_key_invalid).404No such request, trip or vehicle at your company.409The trip already changed: already_taken (another company took it), offer_closed, not_after_… (a step out of order), vehicle_busy (on another trip), provider_stopped (your company can't take requests now).429More than 120 requests a minute; try again shortly.
Need help?
Write to [email protected]