API Reference
Complete reference for all Delivery Locker API endpoints.
Base URL
https://api.delivery.kioskforce.com
Authentication
All endpoints require the X-API-Key header:
X-API-Key: YOUR_API_KEY
Endpoints
Echo
Test API connectivity and verify authentication.
POST /api/delivery/external/v1/echo
Request Body:
{
"message": "test"
}
Parameters:
message(body, optional): Echo message
Response:
{
"message": "test"
}
Search Machines
Find delivery lockers by location and criteria.
POST /api/delivery/external/v1/machines/search
Request Body:
{
"latitude": -6.125556,
"longitude": 106.655830,
"radius": 1000,
"limit": 10,
"only_available_lockers": true,
"packaging": {
"width": 300,
"height": 200,
"depth": 400,
"weight": 1500,
"heating_required": false,
"cooling_required": true
}
}
Parameters:
latitude(required): Latitude coordinatelongitude(required): Longitude coordinateradius(required): Search radius in meterslimit(optional): Maximum number of resultsonly_available_lockers(optional): Only return machines with available cellspackaging(optional): Package requirements for optimal matching
Response:
{
"machines": [
{
"machine": {
"machine_id": "machine_123",
"name": "Awesome Business Park - Waiting Area (South)",
"location": "Benda, Tangerang City, Banten",
"latitude": -6.125556,
"longitude": 106.655830,
"online": true,
"preferred_cells": [
{
"width": 300,
"height": 300,
"depth": 300,
"heating": false,
"cooling": true,
"weight_sensor": true
}
]
},
"distance_in_metres": 150
}
]
}
Get Machine Details
Retrieve detailed information about a specific machine.
GET /api/delivery/external/v1/machines/{machine_id}
Parameters:
machine_id(path, required): Unique machine identifier
Response:
{
"machine": {
"machine_id": "machine_123",
"name": "Awesome Business Park - Waiting Area (South)",
"location": "Benda, Tangerang City, Banten",
"latitude": -6.125556,
"longitude": 106.655830,
"online": true,
"preferred_cells": [...]
}
}
Reserve Cell
Reserve a locker compartment for package delivery.
POST /api/delivery/external/v1/reservations
Request Body:
{
"machine_id": "machine_123",
"platform_order_id": "order_456",
"notes": "Fragile package - handle with care",
"webhook_url": "https://yourapi.com/webhooks/delivery",
"packaging": {
"width": 300,
"height": 200,
"depth": 400,
"weight": 1500,
"heating_required": false,
"cooling_required": true
},
"eta_minutes": 30,
"grace_period_pickup_in_seconds": 120,
"grace_period_dropoff_in_seconds": 120
}
Parameters:
machine_id(required): Target machine identifierplatform_order_id(required): Your internal order ID (max 1024 chars)packaging(required): Package dimensions and requirementsnotes(optional): Additional notes for operations team (max 1024 chars)webhook_url(optional): Custom webhook URL for this reservationeta_minutes(optional): Expected arrival time in minutesgrace_period_pickup_in_seconds(optional): Pickup grace period (default: 120)grace_period_dropoff_in_seconds(optional): Drop-off grace period (default: 120)
Response:
{
"reservation_id": "res_789",
"machine_id": "machine_123",
"machine": { ... },
"drop_off_code": "1234",
"drop_off_qrcode": "DLV-DROP-res_789-1234",
"drop_off_qrcode_url": "https://api.delivery.kioskforce.com/qrcode/ABC123",
"pickup_code": "5678",
"pickup_code_qrcode": "DLV-PICK-res_789-5678",
"pickup_code_qrcode_url": "https://api.delivery.kioskforce.com/qrcode/DEF456"
}
Get Reservation
Retrieve the details of a specific reservation by its ID.
GET /api/delivery/external/v1/reservations/{reservation_id}
Parameters:
reservation_id(path, required): Reservation identifier
Response:
{
"reservation_id": "res_789",
"machine_id": "machine_123",
"machine": { ... },
"platform_order_id": "order_456",
"notes": "Fragile package - handle with care",
"status": "RESERVATION_STATUS_DROPPED_OFF",
"created_at": "2025-07-10T10:00:00Z",
"drop_off_code": "1234",
"drop_off_qrcode": "DLV-DROP-res_789-1234",
"drop_off_qrcode_url": "https://api.delivery.kioskforce.com/qrcode/ABC123",
"pickup_code": "5678",
"pickup_code_qrcode": "DLV-PICK-res_789-5678",
"pickup_code_qrcode_url": "https://api.delivery.kioskforce.com/qrcode/DEF456",
"cell": {
"description": "A03"
},
"dropped_off_at": "2025-07-10T10:30:00Z"
}
Status Values:
RESERVATION_STATUS_PENDING- Reservation created but package not yet dropped offRESERVATION_STATUS_DROPPED_OFF- Package has been dropped off by rider/driverRESERVATION_STATUS_PICKED_UP- Package has been picked up by consumerRESERVATION_STATUS_CANCELLED- Reservation has been cancelledRESERVATION_STATUS_ABANDONED- Pickup has been abandoned
List Ongoing Reservations
Retrieve all ongoing (not finished) reservations for your platform with pagination support.
GET /api/delivery/external/v1/reservations
Query Parameters:
page_size(optional): Maximum number of reservations per page (default: 100, max: 1000)page_token(optional): Token for retrieving next page of resultscreated_after(optional): Unix timestamp to filter reservations created after this time (exclusive)
Ongoing States: Reservations are considered "ongoing" if they are in one of these states:
PENDING- Cell reserved but package not yet dropped offDROPPED_OFF- Package dropped off, waiting for consumer pickupPICKED_UP- Package picked up but still within grace period (consumer can re-open door)
Grace Period Behavior:
- A reservation in
PICKED_UPstate remains in the list only if:(current_time - picked_up_at) <= grace_period_pickup_seconds - This allows tracking reservations where consumers can still re-access the cell
- After grace period expires, the reservation is excluded from this list
Response:
{
"reservations": [
{
"reservation_id": "res_789",
"machine_id": "machine_123",
"platform_order_id": "order_456",
"status": "RESERVATION_STATUS_DROPPED_OFF",
"created_at": "2025-11-11T10:00:00Z",
"dropped_off_at": "2025-11-11T10:30:00Z",
"cell": {
"description": "A03"
}
}
],
"next_page_token": "token_abc123",
"total_count": 150
}
Response Fields:
reservations- Array of ongoing reservations, ordered by creation time (oldest first)next_page_token- Token for next page (empty if no more results)total_count- Approximate total number of ongoing reservations
Pagination Example:
# First page
GET /api/delivery/external/v1/reservations?page_size=50
# Next page
GET /api/delivery/external/v1/reservations?page_size=50&page_token=token_abc123
Filtering by Time:
# Get reservations created after timestamp 1699999999
GET /api/delivery/external/v1/reservations?created_after=1699999999
Use Cases:
- Monitor active deliveries in progress
- Track packages waiting for consumer pickup
- Identify reservations needing attention (e.g., long wait times)
- Build real-time dashboard showing current delivery status
- Implement incremental sync workflows
Drop Off Package
Open locker door for package drop-off by driver/rider.
POST /api/delivery/external/v1/reservations/{reservation_id}/dropoffs
Parameters:
reservation_id(path, required): Reservation identifier
Response:
{
"cell": {
"description": "A03"
}
}
⚠️ State Transition Behavior:
- The API call opens the locker door immediately
- The reservation state does NOT change to
DROPPED_OFFuntil the door is closed by the driver/rider - This allows the driver to ensure the package is properly placed before the state changes
- Once the door closes, the state transitions to
DROPPED_OFFand the dropoff webhook is sent (if configured)
Pick Up Package
Open locker door for package pickup by consumer.
POST /api/delivery/external/v1/reservations/{reservation_id}/pickups
Parameters:
reservation_id(path, required): Reservation identifier
Response:
{
"cell": {
"description": "A03"
}
}
⚠️ State Transition Behavior:
- The API call opens the locker door immediately
- The reservation state changes immediately to
PICKED_UPwhen the door opens successfully - This is different from dropoff - pickup state changes on door open, not door close
- The pickup webhook is sent immediately when the door opens (if configured)
- The grace period starts from this moment, allowing the consumer to re-open the door if needed
Cancel Reservation
Cancel a reservation before package drop-off.
POST /api/delivery/external/v1/reservations/{reservation_id}/deletes
Parameters:
reservation_id(path, required): Reservation identifier
Response:
{}
Note: This only works if the package hasn't been dropped off yet. Use "Abandon Pick Up" if the package is already in the locker.
Abandon Pick Up
Mark a package as abandoned when consumer cannot/will not pick it up.
POST /api/delivery/external/v1/reservations/{reservation_id}/abandons
Parameters:
reservation_id(path, required): Reservation identifier
Response:
{}
Note: Use this when the consumer has requested to abandon the package. The operations team will be notified for cleanup.
Request Maintenance
Report cell or locker issues that require operations team attention.
POST /api/delivery/external/v1/maintenances
Request Body:
{
"cell_id": "cell_unique_id_123",
"machine_id": "machine_123",
"description": "Door mechanism not opening properly - requires inspection",
"severity": "HIGH",
"block_cell_immediately": true
}
Parameters:
cell_id(optional): Unique identifier of the problematic cellmachine_id(optional): Machine identifier for machine-level issuesdescription(required): Detailed description of the issueseverity(optional): Issue severity (LOW,MEDIUM,HIGH,CRITICAL)block_cell_immediately(optional): If true, blocks the cell from further reservations
Response:
{
"maintenance_ticket_id": "maint_456",
"created_at": "2025-11-13T10:00:00Z",
"status": "OPEN"
}
Use Cases:
- Report broken doors or mechanisms
- Flag cells with hardware issues after failed operations
- Escalate repeated failure patterns
- Request proactive maintenance
Open Door (Out-of-Band)
Open locker door for customer support and troubleshooting without changing reservation state.
POST /api/delivery/external/v1/reservations/{reservation_id}/open-door
Request Body:
{
"reason": "Customer support request - customer unable to open door with code",
"platform_reference_id": "support_ticket_789"
}
Parameters:
reservation_id(path, required): Reservation identifierreason(body, optional): Explanation for opening the doorplatform_reference_id(body, optional): Your internal ticket/reference ID
Response:
{
"request_id": "req_xyz789",
"accepted_at": "2025-11-13T15:30:00Z"
}
⚠️ Async Operation:
- The request is accepted immediately and processed asynchronously
- A webhook event
EVENT_TYPE_OPEN_DOOR_FULFILLEDwill be sent when the operation completes - Use the
X-Request-IDheader to correlate the API call with the webhook event
Time Window Constraints: The door can be opened during these periods:
- Cell Allocation Period: From when cell is allocated until drop-off begins
- Drop-off Grace Period: From first DropOff call through grace period (default: 120s)
- Awaiting Pickup Period: From drop-off completion until pickup begins (DROPPED_OFF state)
- Pickup Grace Period: From first PickUp call through grace period (default: 120s)
- Extended Window: Even after grace period expires, until cell is reallocated
Error Handling:
- Immediate errors (in response): Invalid reservation, auth failure, time window violation, device offline
- Deferred errors (via webhook): Hardware failure, door mechanism issues
Use Cases:
- Customer support assistance when codes don't work
- Verify door mechanism is functioning
- Emergency access situations
- Troubleshooting pickup issues
Example Webhook Correlation:
# 1. Make API call with X-Request-ID header
curl -X POST "https://api.delivery.kioskforce.com/api/delivery/external/v1/reservations/res_789/open-door" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Request-ID: req_xyz789" \
-H "Content-Type: application/json" \
-d '{
"reason": "Customer support ticket #456",
"platform_reference_id": "ticket_456"
}'
# 2. Receive immediate response
{
"request_id": "req_xyz789",
"accepted_at": "2025-11-13T15:30:00Z"
}
# 3. Later, receive webhook when operation completes
# See Webhooks documentation for EVENT_TYPE_OPEN_DOOR_FULFILLED details
State Transition Timing
Asymmetric Behavior: Dropoff vs Pickup
The API has different state transition timing for dropoff and pickup operations:
| Operation | Door Opens | State Changes | Webhook Sent | Reason |
|---|---|---|---|---|
| Dropoff | ✅ Immediately | ⏳ When door closes | ⏳ When door closes | Allows driver to verify package placement |
| Pickup | ✅ Immediately | ✅ Immediately | ✅ Immediately | Consumer has retrieved the package |
Dropoff Flow
1. Call /dropoffs API → Door opens immediately (state: PENDING)
2. Driver places package
3. Driver closes door → State changes to DROPPED_OFF + webhook sent
Why? The dropoff state transition is delayed until door closure to ensure the driver has successfully placed the package. If the door closes before the driver is ready, they can re-open it within the grace period.
Pickup Flow
1. Call /pickups API → Door opens immediately → State changes to PICKED_UP + webhook sent
2. Consumer retrieves package
3. Consumer closes door
Why? The pickup state transition is immediate because the act of opening the door means the consumer has accessed the package. The grace period allows re-opening if needed (e.g., if the door accidentally closes).
Important Implications
- Monitoring Dropoffs: Don't assume a dropoff is complete immediately after calling the API - wait for the webhook or check the reservation status
- Monitoring Pickups: The pickup is considered complete as soon as the door opens successfully
- Grace Periods: Both operations allow re-opening the door within their respective grace periods, but the state has already changed for pickup
- Webhook Timing: Dropoff webhooks are delayed until door closure, while pickup webhooks are sent immediately
Error Responses
All endpoints may return these error status codes:
403- Unauthorized (invalid API key)404- Resource not found500- Server error
Error response format (rpcStatus):
{
"code": 5,
"message": "Resource not found",
"details": []
}
Common Error Codes:
3- Invalid argument5- Not found7- Permission denied13- Internal server error16- Unauthenticated
Notes
QR Code Fields
The API returns QR code data in two formats for both drop-off and pickup operations:
-
QR Code Text (
drop_off_qrcode,pickup_code_qrcode): Plain text string that should be encoded into a QR code by your application- Allows you to generate QR codes with your own branding, colors, and styling
- Can be embedded directly into emails, mobile apps, or printed materials
- Gives you full control over the QR code appearance and presentation
-
QR Code URL (
drop_off_qrcode_url,pickup_code_qrcode_url): A URL that displays the QR code as an image- Provides a ready-to-use QR code image
- Useful when you need a quick solution without implementing QR code generation
The QR code URL is constructed by appending the QR code text to the base URL. For example:
- If
drop_off_qrcodeis"DLV-DROP-res_789-1234" - Then
drop_off_qrcode_urlwill be"https://api.delivery.kioskforce.com/qrcode/DLV-DROP-res_789-1234"
This URL can be:
- Displayed directly in an
<img>tag in web applications - Downloaded and printed for physical distribution
- Shared via messaging apps or email
The same pattern applies for pickup QR codes.
Data Models
Machine
{
"machine_id": "string",
"name": "string",
"location": "string",
"latitude": "float",
"longitude": "float",
"online": "boolean",
"preferred_cells": [CellInformation],
"all_cells": [CellInformation],
"occupancy_information": "OccupancyStatus"
}
Fields:
preferred_cells- Non-exhaustive, KF-recommended cells with blended sizes for consideration. Only includes cells with status IDLE at the time of query (not reserved or occupied).all_cells- Complete inventory of all cells configured for this machineoccupancy_information- Real-time occupancy status (includes reserved cells)
OccupancyStatus
Enum indicating the current occupancy level of a machine:
OCCUPANCY_STATUS_LOW(1) - Machine is essentially empty (≤30% occupied)OCCUPANCY_STATUS_MEDIUM(2) - Machine is partially occupied (31-70% occupied)OCCUPANCY_STATUS_HIGH(3) - Machine is almost full (>70% occupied)OCCUPANCY_STATUS_OVERLOADED(4) - Machine is overloaded (>95% occupied)
CellInformation
{
"width": "int32", // millimeters
"height": "int32", // millimeters
"depth": "int32", // millimeters
"heating": "boolean",
"cooling": "boolean",
"weight_sensor": "boolean"
}
Packaging
{
"width": "int32", // millimeters
"height": "int32", // millimeters
"depth": "int32", // millimeters
"weight": "int32", // grams
"heating_required": "boolean",
"cooling_required": "boolean"
}