Kilo Docs

Docs / Get started/ Capabilities & locations

Capabilities & locations

Updated Aug 12, 2026

Why this page exists (read this first)

GET /capabilities is not a “nice to have.” It is the read-only mirror of the business dashboard for one org.

Layer Role
Dashboard → Settings → Delivery Owner turns services, payments, POD, coverage, auto-dispatch on/off
GET /capabilities Your backend learns those same rules (already clamped by plan + trust)
Your bot / form Only asks for fields that are allowed for this org
POST /deliveries Rejects jobs that ignore those rules (service_disabled, bad payment method, etc.)

The public request form already does this: it loads delivery settings, then only shows enabled service kinds, warehouse mode, errand modes, payment options, and saved places. Your API integration should behave the same way.

If you skip discovery, bots invent services the fleet never enabled, ask for cash when only online checkout is on, or guess hostel coordinates — create fails or riders get bad pins.


Mental picture (dashboard → API → chat)

┌─────────────────────────────────────────────────────────────┐
│  Kilo Business dashboard                                    │
│  Settings → Delivery                                        │
│    • Pickup & deliver / Errand toggles                      │
│    • Warehouse drop-off                                     │
│    • Errand modes (at place / link / photo)                 │
│    • Payment methods + when customer pays                   │
│    • POD, coverage, auto-dispatch, pricing                  │
│  Settings → Warehouses                                      │
│  Settings → Service locations (hostels, gates, …)           │
└───────────────────────────┬─────────────────────────────────┘
                            │ same truth (plan-clamped)
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  GET /capabilities  (+ /warehouses + /service-locations)    │
│  → enabledServiceKinds, payments, POD, features, …          │
└───────────────────────────┬─────────────────────────────────┘
                            │ drive the conversation
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  Your bot / checkout                                        │
│  only offer what capabilities say is allowed                │
│  peep coords from warehouses / service locations            │
│  then POST /deliveries                                      │
└─────────────────────────────────────────────────────────────┘

Rule for AI agents: treat capabilities as the allow-list. If a value is missing or false, that option is not allowed for this org — do not offer it and do not send it on create.


Discover org settings

curl -X GET 'https://business.kiloapp.org/api/v1/capabilities' \
  -H 'Authorization: Bearer sk_live_...'

Scope: deliveries:read. Cache for the chat/order session; refresh after create errors about service/coverage/payment.

Field → dashboard → allowed / not allowed

Use this table as the contract. Left column = JSON path on capabilities.

Field Where the owner sets it If … Your bot / create must
enabledServiceKinds Delivery → service type toggles kind not in the array Do not offer that job type; create with that serviceKind → service_disabled
only one kind listed Skip “which service?” — use that kind
both listed Ask pickup & deliver vs errand (or infer clearly)
defaultServiceKind Delivery → default service user has not chosen yet Pre-select this kind
allowWarehouseDropoff Delivery → “Warehouse drop-off” false Do not use deliveryMode: "warehouse"
true May offer warehouse pickup; load GET /warehouses
errand.enabledModes Delivery → errand modes mode missing Do not collect that errand flow (at_place / link / photo)
errand.defaultMode same starting an errand Pre-select this mode
errand.curatedLinks Delivery → curated shop links link mode Offer these as suggested shops (name + url)
errand.allowUnknownShopLocation Delivery → unknown shop false Require a known shop pin / curated link — don’t accept vague “any shop”
paymentCollection.onlineCheckout Delivery → Operations / payments false Do not send customerCollectionMethod: "online_checkout"
paymentCollection.cashOnDelivery same false Do not send cash_on_delivery
both false (shouldn’t happen after reconcile) Treat as misconfigured — tell human to fix Delivery settings
currency Operations → currency any Display money in this code; don’t invent another
paymentTiming Delivery → when customer pays before_dispatch Expect pay-before-rider; rider may wait until fee is paid
after_delivery Job can run; collect fee later
off_platform No portal checkout step — fee handled outside Kilo
pod.enabled Delivery → Payment on delivery (POD) false Do not enable POD / isPodEnabled on create
true May offer cash-to-collect on pickup_delivery only (never errands)
requireDeliveryOtp Delivery → Require OTP at drop-off true (default) Expect a drop-off code (Kilo SMS/WhatsApp or delivery.otp_issued if OTP-via-webhook)
false Photo proof only — no code, no delivery.otp_issued, do not ask the customer for an OTP
publicRequest.requireApproval Delivery → intake true Creates may sit awaiting staff price/approval (like slower portal intake)
false Faster intake (still subject to pricing rules)
publicRequest.autoDispatch Delivery → Auto-dispatch true Nearby rider may be assigned on create — see Auto-assign
false Staff (or your assign flow) must dispatch
publicRequest.pricingMode / pricingAmountNgn Delivery → public pricing fixed / starting_from Show amount when explaining fees; still don’t invent coverage
coverage.* Delivery → Coverage any Soft hint only — create still validates addresses; out-of-area → coverage errors
plan Billing basic Fewer warehouses; service locations may be off
pro Full features (see features)
features.serviceLocations plan feature false GET /service-locations returns [] — use geocoding / free-form carefully
true but list empty Tell human to add service locations in the dashboard before chat bookings work well
features.maxWarehouses plan 1 Basic: only first warehouse is usable
null Pro: no API-side warehouse cap
rateLimits.requestsPerMinute Billing plan number Per-key cap for this org; HTTP 429 when exceeded
null Custom / no per-key cap (Enterprise)

Typed shape: API reference → GET /capabilities.

Annotated example

JSON
{
  "ok": true,
  "capabilities": {
    "organization": { "id": "…", "name": "Campus Couriers", "slug": "campus" },
    "plan": "pro",
    "enabledServiceKinds": ["pickup_delivery"],
    "defaultServiceKind": "pickup_delivery",
    "allowWarehouseDropoff": true,
    "errand": {
      "enabledModes": ["at_place"],
      "defaultMode": "at_place",
      "allowUnknownShopLocation": false,
      "curatedLinks": []
    },
    "paymentCollection": { "onlineCheckout": true, "cashOnDelivery": true },
    "currency": "NGN",
    "paymentTiming": "before_dispatch",
    "coverage": {
      "mode": "radius_from_hub",
      "jobScopePolicy": "pickup_and_dropoff_in_coverage",
      "radiusKm": 15,
      "regionCount": 0
    },
    "pod": { "enabled": false },
    "requireDeliveryOtp": true,
    "publicRequest": {
      "requireApproval": false,
      "autoDispatch": true,
      "pricingMode": "fixed",
      "pricingAmountNgn": 1500
    },
    "features": { "serviceLocations": true, "maxWarehouses": null },
    "rateLimits": { "requestsPerMinute": 180, "windowSeconds": 60 }
  }
}

How an AI should read that example

  • Only offer pickup & deliver (errand is off even though errand object exists — enabledServiceKinds wins).
  • Warehouse pickup allowed → call GET /warehouses.
  • Customer may pay online or cash; timing is before dispatch.
  • No POD → never ask “collect item money from receiver.”
  • Drop-off OTP required → customer needs the code (or your bot sends it via delivery.otp_issued).
  • Auto-dispatch on → a rider may appear without staff Assign.
  • Service locations on → peep hostel/gate coords from GET /service-locations.

Warehouses

Dashboard: Settings → Warehouses (active rows only).

curl -X GET 'https://business.kiloapp.org/api/v1/warehouses' \
  -H 'Authorization: Bearer sk_live_...'
When Action
allowWarehouseDropoff is false Do not call this for pickup mode; do not send deliveryMode: "warehouse"
true List warehouses; user picks one → create with deliveryMode: "warehouse", warehouseId, and pickupAddress / pickupCoords copied from that row’s address + longitude/latitude

Same pins the public form uses for hub pickup.

Need the exact response shape right now? Jump to API reference → GET /warehouses.


Service locations (hostels, gates, landmarks)

Dashboard: Settings → Service locations (Pro). Same places the public form’s place picker uses.

Why bots need this: create requires real pickupCoords / deliveryCoords. Chat says *“Queen’s Hall room 12”* — not lat/lng. The business saves the place once with a map pin; the bot peeps coords and copies them into create. Do not invent {lng:0,lat:0} or re-geocode the composed address string (wrong building / out of coverage).

curl -X GET 'https://business.kiloapp.org/api/v1/service-locations' \
  -H 'Authorization: Bearer sk_live_...'
unit.mode Allowed bot behavior
none Use the place as-is
free_text Ask for unit.label (e.g. Room); required if unit.required
list Offer unit.ranges + unit.values; custom text only if unit.allowCustom

Compose address text like the public form:

{name} — {address} — {unit}

Keep the same place coords after the user picks a room (unit only changes the string).

Need the exact response shape right now? Jump to API reference → GET /service-locations.

Allowed vs not

Situation Allowed?
Match user phrase to a service location → copy coords Yes (preferred for chat)
Free-form address + real geocoder when no place fits Yes
Fake / hardcoded coordinates No
Use service locations for an errand shop No — follow errand.enabledModes for the shop
Use a service location for customer dropoff on an errand Yes if they live in a known place

Conversation pattern (bots)

  1. GET /capabilities (cache for the session).
  2. Build menus only from allowed values in the table above.
  3. If pickup_delivery → load service locations + warehouses; match vague place names; peep coords.
  4. If errand → errand modes only for the shop side; dropoff may still be a service location.
  5. Choose customerCollectionMethod only if that payment flag is true.
  6. Then POST /deliveries with a complete payload.

If create returns service_disabled, payment errors, or coverage errors — re-fetch capabilities and tell the human which Delivery setting to change.