Same as the dashboard Assign rider button. Your server picks a rider. Kilo does not invent extra limits.
Who appears on GET /riders
Only riders who accepted the fleet invite. Pending invites are never listed and cannot be assigned. There is no query flag for this — Kilo enforces it.
Everyone in the response can take a job.
One rider, many jobs — yes
A rider can be on more than one delivery at the same time. That already works in the dashboard. The API is the same.
POST /assign does not reject a rider because they are on_delivery or active_delivery_count is 2. Stacking is allowed.
Do not skip assignment just because everyone is busy. If you leave the job with no rider, it will not move.
What pickup_lat / pickup_lng do (read this)
They do not hide riders. They measure.
When you send pickup coordinates:
- Each rider with GPS gets
distance_km(straight line to that pickup). - The list is sorted closer first.
- Riders with no GPS stay in the list (
distance_km: null).
So: GET /riders?pickup_lat=6.45&pickup_lng=3.40 means “show my accepted fleet, tell me who is closer.” It does not mean “only people inside a circle.”
Optional filters
There is no default radius and no default idle filter. We never drop far or busy riders unless you ask.
| Query | What it does | Default |
|---|---|---|
pickup_lat + pickup_lng |
Fill distance_km and sort closer first. Does not hide anyone |
no distances |
radius_km |
Hide riders whose known distance is greater than this. Needs pickup coords. Riders with unknown GPS are kept. | no cutoff |
idle_only=true |
Hide anyone not idle in the app — including riders already on a job | keep busy riders |
- Omit
radius_km(normal): everyone stays. You pick usingdistance_km. - Pass
radius_km=8only if you really want a cutoff. Most fleets should omit it so someone still gets the job. - Do not add
idle_only=trueunless you only want free riders and you accept that the job may stay pending.
Baby steps for an agent
- Listen for webhook
delivery.created. - If
data.object.rider_assignedis alreadytrue, stop (someone else assigned). - Call:
curl -X GET 'https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}' \
-H 'Authorization: Bearer sk_live_...'curl -X GET 'https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}' \
-H 'Authorization: Bearer sk_live_...'const response = await fetch("https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}", {
method: "GET",
headers: {
"Authorization": "Bearer sk_live_..."
},
});
const data = await response.json();
console.log(data);const response = await fetch("https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}", {
method: "GET",
headers: {
"Authorization": "Bearer sk_live_..."
},
});
const data = await response.json();
console.log(data);import requests
url = "https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}"
headers = {
"Authorization": "Bearer sk_live_..."
}
response = requests.request("GET", url, headers=headers)
print(response.json())import requests
url = "https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}"
headers = {
"Authorization": "Bearer sk_live_..."
}
response = requests.request("GET", url, headers=headers)
print(response.json())<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer sk_live_...",
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/riders?pickup_lat={pickup.latitude}&pickup_lng={pickup.longitude}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer sk_live_...",
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;- Look at the array. First rows are closer (when GPS exists).
- Pick one rider. Prefer smallest
distance_km. A rider withpresence: "on_delivery"is still OK. - Assign:
curl -X POST 'https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk_live_...' \
-d '{
"riderId": "the rider id from step 5"
}'curl -X POST 'https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk_live_...' \
-d '{
"riderId": "the rider id from step 5"
}'const response = await fetch("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
},
body: JSON.stringify({
"riderId": "the rider id from step 5"
}),
});
const data = await response.json();
console.log(data);const response = await fetch("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
},
body: JSON.stringify({
"riderId": "the rider id from step 5"
}),
});
const data = await response.json();
console.log(data);import requests
url = "https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
}
payload = {
"riderId": "the rider id from step 5"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.json())import requests
url = "https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
}
payload = {
"riderId": "the rider id from step 5"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.json())<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer sk_live_...",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"riderId\": \"the rider id from step 5\"\n}");
$response = curl_exec($ch);
curl_close($ch);
echo $response;<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer sk_live_...",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"riderId\": \"the rider id from step 5\"\n}");
$response = curl_exec($ch);
curl_close($ch);
echo $response;Use the rider’s id from this list. Do not invent ids. Do not use a user id from somewhere else.
List riders (all query params)
curl -X GET 'https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721' \ -H 'Authorization: Bearer sk_live_...'
curl -X GET 'https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721' \
-H 'Authorization: Bearer sk_live_...'const response = await fetch("https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721", {
method: "GET",
headers: {
"Authorization": "Bearer sk_live_..."
},
});
const data = await response.json();
console.log(data);const response = await fetch("https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721", {
method: "GET",
headers: {
"Authorization": "Bearer sk_live_..."
},
});
const data = await response.json();
console.log(data);import requests
url = "https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721"
headers = {
"Authorization": "Bearer sk_live_..."
}
response = requests.request("GET", url, headers=headers)
print(response.json())import requests
url = "https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721"
headers = {
"Authorization": "Bearer sk_live_..."
}
response = requests.request("GET", url, headers=headers)
print(response.json())<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer sk_live_...",
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/riders?pickup_lat=6.4474&pickup_lng=3.4721");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer sk_live_...",
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;Scope: deliveries:read
Each rider includes:
| Field | Meaning |
|---|---|
id |
Send this as riderId on assign |
name / vehicle_type |
Display |
presence |
available (idle in app), on_delivery (already has a job — still OK to assign), unavailable, not_in_app |
active_delivery_count |
How many in-progress jobs they already have. More than 0 is OK |
location |
Last GPS if we have it (fresh = updated in the last 30 minutes) |
distance_km |
km to the pickup you passed, or null if we have no GPS |
Assign
curl -X POST 'https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk_live_...' \
-d '{
"riderId": "uuid-from-get-riders"
}'curl -X POST 'https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk_live_...' \
-d '{
"riderId": "uuid-from-get-riders"
}'const response = await fetch("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
},
body: JSON.stringify({
"riderId": "uuid-from-get-riders"
}),
});
const data = await response.json();
console.log(data);const response = await fetch("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
},
body: JSON.stringify({
"riderId": "uuid-from-get-riders"
}),
});
const data = await response.json();
console.log(data);import requests
url = "https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
}
payload = {
"riderId": "uuid-from-get-riders"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.json())import requests
url = "https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_..."
}
payload = {
"riderId": "uuid-from-get-riders"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.json())<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer sk_live_...",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"riderId\": \"uuid-from-get-riders\"\n}");
$response = curl_exec($ch);
curl_close($ch);
echo $response;<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/assign");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer sk_live_...",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n \"riderId\": \"uuid-from-get-riders\"\n}");
$response = curl_exec($ch);
curl_close($ch);
echo $response;Scope: deliveries:write
The only Kilo rules (same as the dashboard):
- Delivery status is
awaiting_price,pending, ormatched(before pickup starts). - Delivery fee is already set (greater than zero).
riderIdis on this fleet and they accepted the invite.
Kilo does not check “are they free?” You can assign a busy rider.
Success: delivery status is matched, rider_assigned is true, rider has id / name / vehicle_type. Then webhook delivery.status_changed.
Need exact request or response shapes?
- List riders: API reference → GET /riders
- Assign request/response: API reference → POST /deliveries/{deliveryId}/assign
- Unassign response: API reference → POST /deliveries/{deliveryId}/unassign
- Shared delivery object: API reference → Delivery object
- Error shape: API reference → Error envelope
Errors
| HTTP | code |
When |
|---|---|---|
| 400 | validation_error |
Missing / invalid riderId |
| 400 | assign_not_allowed |
Pickup already started |
| 400 | fee_required |
No delivery fee yet |
| 400 | rider_not_found |
Id is not on this fleet |
| 400 | rider_not_active |
Invite not accepted |
| 404 | not_found |
Delivery missing |
Unassign
curl -X POST 'https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign' \
-H 'Authorization: Bearer sk_live_...'curl -X POST 'https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign' \
-H 'Authorization: Bearer sk_live_...'const response = await fetch("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign", {
method: "POST",
headers: {
"Authorization": "Bearer sk_live_..."
},
});
const data = await response.json();
console.log(data);const response = await fetch("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign", {
method: "POST",
headers: {
"Authorization": "Bearer sk_live_..."
},
});
const data = await response.json();
console.log(data);import requests
url = "https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign"
headers = {
"Authorization": "Bearer sk_live_..."
}
response = requests.request("POST", url, headers=headers)
print(response.json())import requests
url = "https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign"
headers = {
"Authorization": "Bearer sk_live_..."
}
response = requests.request("POST", url, headers=headers)
print(response.json())<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer sk_live_...",
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;<?php
$ch = curl_init("https://business.kiloapp.org/api/v1/deliveries/{deliveryId}/unassign");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer sk_live_...",
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;Only while status is still matched (before pickup). Status goes back to pending (or awaiting_price if there is no fee).
Related
- Auto-assign riders — Kilo’s built-in picker (different: it only picks idle nearby riders on create)
- Auto-assign agent — copy-ready prompt
- Webhooks
- API reference