RentADriver API
Base URL https://api.rentadriver.ai/v1. JSON in, JSON out. Money is in integer minor units (cents). Timestamps are ISO-8601 UTC. Every response has success, and delivery responses carry next_action.
Quick start
# 1. Price it — no auth
curl -X POST https://api.rentadriver.ai/v1/quotes -H 'Content-Type: application/json' -d '{
"pickup": {"address": "Queen Victoria Market, Melbourne", "contact_name": "Stall 12"},
"dropoffs": [{"address": "Flinders Street Station, Melbourne", "contact_name": "Alex", "contact_phone": "+61400000000"}],
"items": [{"description": "Box of pastries", "size_class": "M", "category": "food"}],
"urgency": "asap"
}'
# 2. Get a key (once) — or use the Console
curl -X POST https://api.rentadriver.ai/v1/signup -H 'Content-Type: application/json' -d '{"name":"My Agent","email":"me@example.com"}'
# 3. Book it — funds held, dispatch starts
curl -X POST https://api.rentadriver.ai/v1/deliveries -H 'x-api-key: rd_live_…' -H 'Content-Type: application/json' \
-d '{"quote_id": "<quote.id>", "instructions": "Ring twice", "idempotency_key": "order-1042"}'
# 4. Follow it
curl https://api.rentadriver.ai/v1/deliveries/<id>/track -H 'x-api-key: rd_live_…'
curl -N https://api.rentadriver.ai/v1/deliveries/<id>/events?stream=1 -H 'x-api-key: rd_live_…' # SSE
# 5. Proof, then confirm (auto after 24h)
curl https://api.rentadriver.ai/v1/deliveries/<id>/proof -H 'x-api-key: rd_live_…'
curl -X POST https://api.rentadriver.ai/v1/deliveries/<id>/confirm -H 'x-api-key: rd_live_…'Authentication
Send your key as x-api-key: rd_live_… (or Authorization: Bearer rd_live_…). Keys are created from the Console, the POST /v1/signup endpoint, or the MCP signup tool. Up to 10 active keys per account; revoke from DELETE /v1/api-keys/:id. Quotes, coverage, service areas, stats and recipient tracking need no key.
Statuses
| Status | Meaning | Money |
|---|---|---|
| quoted | Created, not funded | — |
| funded | Scheduled; dispatch starts 45 min before the window | held |
| dispatching | Offering to on-shift drivers in expanding radius waves | held |
| assigned | A driver accepted | held |
| en_route_pickup → at_pickup → picked_up | Driver steps with pickup photo | held |
| en_route_dropoff → at_dropoff → delivered | Drop-off proof captured | held |
| confirmed / paid | You confirmed (or 24h passed); driver is paid | captured |
| no_driver_found | Six waves, no acceptance | released |
| cancelled | You cancelled. Free before assignment; 30% of driver pay after | released (minus fee) |
| failed | Driver could not complete; a review is opened | held until resolved |
| disputed | You opened a dispute | held until resolved |
| returned | Item brought back to pickup | captured |
Pricing model
Each city has a pricing profile. price = max(min_price, base + per_km·km + per_min·(drive + wait)) × size × urgency × vehicle × surge. Driver pay is the higher of the hourly floor (plus mileage) and a share of the price, and the price is lifted if needed so the platform margin never drops below the city minimum. The quote’s breakdown shows every factor, including supply_nearby and surge. Quotes expire after 15 minutes.
Intercity (city to city)
Set service to auto (default) or intercity on POST /v1/quotes or POST /v1/deliveries. When the pickup is in one launch city and the last drop-off is inside, or within 80 km of, another launch city in the same country, the quote comes back with service_kind: "intercity" and an intercity object: lane, speed, departure_at, arrival_by, transit time with rest breaks, and the driver milestone schedule. Up to 3 drop-offs.
speed:express(dedicated driver leaves within 2 h, +25 %),standard(dedicated, within 24 h),shared(consolidated lane run at fixed departures, −38 %, no deadhead).- Price = origin city base × 4 + tiered km (62 % / 48 % / 40 % of the local per-km rate for ≤100 / ≤300 / >300 km) + deadhead allowance (10 % of the distance to a launch city, 25 % elsewhere) + tolls + insurance (1.5 % of declared
value_centsabove 50 000), × size × vehicle, then the speed tier. Every component is inbreakdown.intercity. - Money: the full price is held at booking. 40 % of the driver pay is released at
picked_up(eventadvance_released), the rest on confirm. Cancellation after assignment costs 20 % of the price; none after pickup. GET /v1/intercity/laneslists lanes both ways with sample prices per speed and the next shared departures. MCP:list_intercity_lanes,get_intercity_quote.
Proof of delivery
Each stop declares proof_required: any of photo, signature, recipient_name, otp, id_check. Defaults: photo at pickup; photo + recipient name at drop-off. The driver app cannot complete a stop without the required proof. GET /v1/deliveries/:id/proof returns signed URLs valid for one hour. For OTP handoff, put the code in metadata.otp and tell the recipient.
Webhooks
Register with POST /v1/webhooks or pass webhook_url per delivery. Each POST carries X-RentADriver-Event and X-RentADriver-Signature: t=<unix>,v1=<hex> where v1 = HMAC-SHA256(secret, t + "." + rawBody). Retries: 1m, 5m, 30m, 2h, 12h. Events: delivery.created|funded|dispatching|assigned|en_route_pickup|at_pickup|picked_up|en_route_dropoff|at_dropoff|delivered|confirmed|paid|no_driver_found|cancelled|failed|disputed|message|location. Prefer SSE (?stream=1) when your agent is long-running.
Shopify
Merchants install the RentADriver app for Shopify from Shopify admin (or from /shopify). It registers a carrier service that answers checkout with a live Same-day by RentADriver rate for addresses inside the shop’s radius, books the delivery on orders/paid (created_via: "shopify", external_ref = order name, metadata.shopify carries the order id), fulfils the order with the tracking link at pickup and posts fulfillment events until delivered. Agents on the same account can list and book Shopify orders: GET /v1/shopify/orders, POST /v1/shopify/orders/:ref/book (order name like #1042; orders the app never saw are fetched from Shopify). MCP: list_shopify_orders, book_shopify_order.
Errors
| HTTP | code | What to do |
|---|---|---|
| 400 | validation_error / bad_request | Fix the payload; details lists the paths |
| 401 | unauthorized / invalid_api_key | Send a valid x-api-key |
| 402 | insufficient_funds | Deposit (card checkout URL or x402), then POST /fund |
| 403 | spending_cap | Raise the cap in account controls |
| 409 | invalid_state / conflict / already_taken | Read status and next_action; do not retry blindly |
| 410 | quote_expired / offer_expired | Request a new quote |
| 422 | outside_coverage / prohibited_item / proof_required | Change the request; see details |
| 429 | rate_limited | Back off; headers include X-RateLimit-Remaining |
Prohibited items
Alcohol, tobacco and vapes, cannabis, weapons and ammunition, explosives and fireworks, controlled or prescription-only medicines without a licensed sender, hazardous or flammable goods, loose lithium batteries, live animals, human remains, cash above the city cap, and anything illegal to transport. Creation is refused with 422 prohibited_item. Food is allowed as a food category without a temperature guarantee.
MCP
npx -y rentadriver-mcp exposes every endpoint below as a tool, plus create_store_pickup, resources (delivery://, coverage://, openapi://spec) and the plan_delivery prompt. Remote transport: https://mcp.rentadriver.ai/mcp with x-api-key. Full MCP tools reference with a description, inputs, returns and auth for each tool.
Reference
Discovery
GET/v1/intercity/lanesIntercity lanes between launch cities with sample prices per speed tier and next shared departures
Responses: 200 OK
GET/v1/service-areasList launch cities
Responses: 200 OK
GET/v1/coverageIs an address/point covered?
Params: addresslatlng
Responses: 200 OK
GET/v1/statsLive platform stats
Responses: 200 OK
Quotes
POST/v1/quotesPrice a delivery (no auth needed)
{
"type": "object",
"required": [
"pickup",
"dropoffs"
],
"properties": {
"pickup": {
"type": "object",
"required": [],
"properties": {
"address": {
"type": "string",
"description": "Street address; geocoded server-side if lat/lng omitted"
},
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"contact_name": {
"type": "string"
},
"contact_phone": {
"type": "string"
},
"instructions": {
"type": "string"
},
"window_start": {
"type": "string",
"format": "date-time"
},
"window_end": {
"type": "string",
"format": "date-time"
},
"proof_required": {
"type": "array",
"items": {
"type": "string",
"enum": [
"photo",
"signature",
"recipient_name",
"otp",
"id_check"
]
}
}
}
},
"dropoffs": {
"type": "array",
"minItems": 1,
"maxItems": 8,
"items": {
"type": "object",
"required": [],
"properties": {
"address": {
"type": "string",
"description": "Street address; geocoded server-side if lat/lng omitted"
},
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"contact_name": {
"type": "string"
},
"contact_phone": {
"type": "string"
},
"instructions": {
"type": "string"
},
"window_start": {
"type": "string",
"format": "date-time"
},
"window_end": {
"type": "string",
"format": "date-time"
},
"proof_required": {
"type": "array",
"items": {
"type": "string",
"enum": [
"photo",
"signature",
"recipient_name",
"otp",
"id_check"
]
}
}
}
}
},
"service": {
"type": "string",
"enum": [
"auto",
"local",
"intercity"
],
"default": "auto",
"description": "auto picks intercity when the drop-off is in another launch city"
},
"speed": {
"type": "string",
"enum": [
"express",
"standard",
"shared"
],
"default": "standard",
"description": "Intercity only: express = dedicated driver leaves within 2h; standard = dedicated within 24h; shared = consolidated lane run at fixed departures (cheapest)"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"description"
],
"properties": {
"descriptioResponses: 200 OK422 outside_coverage / prohibited_item
Account
GET/v1/signup/verifyVerify email from the link sent at signup; unlocks the welcome credit
Params: token
Responses: 200 OK
POST/v1/signupCreate an account + API key (shown once); email verification unlocks the $20 credit
{
"type": "object",
"required": [
"name",
"email"
],
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"kind": {
"type": "string",
"enum": [
"agent",
"app",
"business"
]
},
"agent_framework": {
"type": "string"
}
}
}Responses: 201 OK
GET/v1/accountAccount, balance, capabilities🔑 key
Responses: 200 OK
PATCH/v1/account/controlsSpending caps🔑 key
{
"type": "object",
"properties": {
"spending_cap_per_delivery_cents": {
"type": "integer",
"nullable": true
},
"daily_cap_cents": {
"type": "integer",
"nullable": true
}
}
}Responses: 200 OK
GET/v1/api-keysList keys🔑 key
Responses: 200 OK
POST/v1/api-keysCreate key (max 10)🔑 key
Responses: 201 OK
DELETE/v1/api-keys/{id}Revoke key🔑 key
Params: id
Responses: 200 OK
Deliveries
POST/v1/deliveriesCreate (and fund) a delivery🔑 key
{
"allOf": [
{
"type": "object",
"required": [
"pickup",
"dropoffs"
],
"properties": {
"pickup": {
"type": "object",
"required": [],
"properties": {
"address": {
"type": "string",
"description": "Street address; geocoded server-side if lat/lng omitted"
},
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"contact_name": {
"type": "string"
},
"contact_phone": {
"type": "string"
},
"instructions": {
"type": "string"
},
"window_start": {
"type": "string",
"format": "date-time"
},
"window_end": {
"type": "string",
"format": "date-time"
},
"proof_required": {
"type": "array",
"items": {
"type": "string",
"enum": [
"photo",
"signature",
"recipient_name",
"otp",
"id_check"
]
}
}
}
},
"dropoffs": {
"type": "array",
"minItems": 1,
"maxItems": 8,
"items": {
"type": "object",
"required": [],
"properties": {
"address": {
"type": "string",
"description": "Street address; geocoded server-side if lat/lng omitted"
},
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"contact_name": {
"type": "string"
},
"contact_phone": {
"type": "string"
},
"instructions": {
"type": "string"
},
"window_start": {
"type": "string",
"format": "date-time"
},
"window_end": {
"type": "string",
"format": "date-time"
},
"proof_required": {
"type": "array",
"items": {
"type": "string",
"enum": [
"photo",
"signature",
"recipient_name",
"otp",
"id_check"
]
}
}
}
}
},
"service": {
"type": "string",
"enum": [
"auto",
"local",
"intercity"
],
"default": "auto",
"description": "auto picks intercity when the drop-off is in another launch city"
},
"speed": {
"type": "string",
"enum": [
"express",
"standard",
"shared"
],
"default": "standard",
"description": "Intercity only: express = dedicated driver leaves wResponses: 201 OK402 insufficient_funds — deposit first403 spending_cap422 outside_coverage / prohibited_item
GET/v1/deliveriesList deliveries🔑 key
Params: statussincelimitoffset
Responses: 200 OK
POST/v1/deliveries/batchCreate up to 25 deliveries🔑 key
{
"type": "object",
"properties": {
"deliveries": {
"type": "array",
"items": {
"allOf": [
{
"type": "object",
"required": [
"pickup",
"dropoffs"
],
"properties": {
"pickup": {
"type": "object",
"required": [],
"properties": {
"address": {
"type": "string",
"description": "Street address; geocoded server-side if lat/lng omitted"
},
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"contact_name": {
"type": "string"
},
"contact_phone": {
"type": "string"
},
"instructions": {
"type": "string"
},
"window_start": {
"type": "string",
"format": "date-time"
},
"window_end": {
"type": "string",
"format": "date-time"
},
"proof_required": {
"type": "array",
"items": {
"type": "string",
"enum": [
"photo",
"signature",
"recipient_name",
"otp",
"id_check"
]
}
}
}
},
"dropoffs": {
"type": "array",
"minItems": 1,
"maxItems": 8,
"items": {
"type": "object",
"required": [],
"properties": {
"address": {
"type": "string",
"description": "Street address; geocoded server-side if lat/lng omitted"
},
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"contact_name": {
"type": "string"
},
"contact_phone": {
"type": "string"
},
"instructions": {
"type": "string"
},
"window_start": {
"type": "string",
"format": "date-time"
},
"window_end": {
"type": "string",
"format": "date-time"
},
"proof_required": {
"type": "array",
"items": {
"type": "string",
"enum": [
"photo",
"signature",
"recipient_name",
"otp",
"id_check"
]
}
}
}
}
},
"service": {
"type": Responses: 200 OK
GET/v1/deliveries/{id}Get a delivery (id or short code)🔑 key
Params: id
Responses: 200 OK
PATCH/v1/deliveries/{id}Update instructions / contingency / drop-off contact🔑 key
Params: id
{
"type": "object",
"properties": {
"instructions": {
"type": "string"
},
"contingency": {
"type": "object"
},
"dropoff_contact": {
"type": "object"
},
"window_end": {
"type": "string"
},
"metadata": {
"type": "object"
}
}
}Responses: 200 OK
POST/v1/deliveries/{id}/fundFund from wallet and start dispatch🔑 key
Params: id
Responses: 200 OK402 insufficient_funds
POST/v1/deliveries/{id}/cancelCancel (free before a driver is assigned; 30% driver fee after)🔑 key
Params: id
{
"type": "object",
"properties": {
"reason": {
"type": "string"
}
}
}Responses: 200 OK409 invalid_state (already picked up)
POST/v1/deliveries/{id}/confirmConfirm a delivered job and release payment (auto after 24h)🔑 key
Params: id
Responses: 200 OK
POST/v1/deliveries/{id}/tipTip the driver🔑 key
Params: id
{
"type": "object",
"required": [
"amount_cents"
],
"properties": {
"amount_cents": {
"type": "integer"
}
}
}Responses: 200 OK
POST/v1/deliveries/{id}/rateRate the driver 1–5🔑 key
Params: id
{
"type": "object",
"required": [
"stars"
],
"properties": {
"stars": {
"type": "integer"
},
"comment": {
"type": "string"
}
}
}Responses: 200 OK
POST/v1/deliveries/{id}/disputeOpen a dispute (funds stay held)🔑 key
Params: id
{
"type": "object",
"required": [
"reason"
],
"properties": {
"reason": {
"type": "string"
},
"evidence": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
}
}
}
}Responses: 201 OK
POST/v1/deliveries/{id}/returnCreate the reverse leg (return to pickup)🔑 key
Params: id
Responses: 201 OK
GET/v1/deliveries/{id}/messagesMessages with the driver🔑 key
Params: id
Responses: 200 OK
POST/v1/deliveries/{id}/messagesMessage the driver🔑 key
Params: id
{
"type": "object",
"required": [
"body"
],
"properties": {
"body": {
"type": "string"
}
}
}Responses: 201 OK
Tracking
GET/v1/deliveries/{id}/trackLive status, ETA, driver position🔑 key
Params: id
Responses: 200 OK
GET/v1/deliveries/{id}/eventsEvent timeline; add ?stream=1 for SSE🔑 key
Params: idstream
Responses: 200 OK
GET/v1/deliveries/{id}/proofProof of delivery (signed photo URLs, signature, recipient)🔑 key
Params: id
Responses: 200 OK
GET/v1/events/streamSSE feed of every event on your account🔑 key
Responses: 200 text/event-stream
GET/v1/track/{code}Public recipient tracking by short code
Params: code
Responses: 200 OK
Wallet
GET/v1/walletBalance + recent ledger🔑 key
Responses: 200 OK
POST/v1/wallet/depositCard deposit via Stripe Checkout (returns a URL for a human to pay)🔑 key
{
"type": "object",
"required": [
"amount_cents"
],
"properties": {
"amount_cents": {
"type": "integer",
"minimum": 500
}
}
}Responses: 200 OK501 deposits_not_configured
POST/v1/x402/wallet/depositUSDC on Base via x402 (HTTP 402 flow; no human needed)🔑 key
{
"type": "object",
"properties": {
"amountCents": {
"type": "integer"
}
}
}Responses: 200 OK402 Payment requirements (x402 v2)404 x402_not_enrolled
Webhooks
GET/v1/webhooksList webhooks🔑 key
Responses: 200 OK
POST/v1/webhooksRegister a webhook (HMAC-signed)🔑 key
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"events": {
"type": "array",
"items": {
"type": "string"
}
}
}
}Responses: 201 OK
DELETE/v1/webhooks/{id}Remove webhook🔑 key
Params: id
Responses: 200 OK
Shopify
GET/v1/shopify/shopsShopify shops connected to this account🔑 key
Responses: 200 OK
GET/v1/shopify/ordersShopify orders seen by the app with their delivery status🔑 key
Params: statuslimit
Responses: 200 OK
POST/v1/shopify/orders/{ref}/bookBook a delivery for a Shopify order (by order name like #1001, order id, or our row id); fetches the order from Shopify if needed🔑 key
Params: ref
{
"type": "object",
"properties": {
"force": {
"type": "boolean",
"description": "Ignore the shop's radius / size limits"
},
"shop": {
"type": "string",
"description": "myshopify domain when several shops are connected"
}
}
}Responses: 200 Not booked — see order.status (skipped / needs_funds / failed) and order.last_error201 OK