Skip to main content

Booking Recommendations API

The Booking Recommendations API starts a read-only search for ranked delivery windows. It accepts a destination, fulfilling branch, delivery date, and cart, then returns an asynchronous poll handle.

Authentication

Send an account API key with HTTP Basic authentication:

Authorization: Basic <base64(externalUserId:apiKey)>

The fulfillingBranchId must be the API-key account or one of its descendant accounts. Recommendations only consider routes in that branch's subtree, even when a parent API key can access a larger account family. Sandbox API keys produce sandbox-isolated recommendations.

Start a recommendation

POST /v1/booking/recommend
Content-Type: application/json
{
"cart": {
"items": [
{
"description": "Air conditioner",
"dimensionsCm": {
"height": 90,
"length": 120,
"width": 60
},
"quantity": 1,
"weightKg": 75
}
],
"orderValueCents": 12500
},
"contact": {
"email": "[email protected]",
"name": "Ada Buyer"
},
"date": "2026-08-15",
"deliveryMethod": "cargo-van",
"destination": {
"addressLine1": "123 Main St",
"city": "Los Angeles",
"postalCode": "90012",
"state": "CA"
},
"fulfillingBranchId": "acc_branch",
"origin": {
"addressLine1": "456 Branch Ave",
"city": "Oakland",
"postalCode": "94607",
"state": "CA"
},
"timeframeBucket": "FLEXIBLE"
}

Dimensions are centimeters, weight is kilograms, and order value is integer cents. A single aggregate cart item is valid when SKU-level data is unavailable. timeframeBucket is optional; accepted values are MORNING, AFTERNOON, EVENING, AFTER_8PM, and FLEXIBLE. The account's slot policy controls which buckets are available and supplies the default when the field is omitted. origin is optional and overrides the pickup address. When it is omitted, the fulfilling branch's configured location is used. Coordinates may be supplied with the override; otherwise the API geocodes the address.

A valid request returns 202:

{
"jobId": "recommendation-problem-id",
"problemId": "recommendation-problem-id",
"status": "processing"
}

Submitting identical result-affecting input returns the same deterministic handle. Live and sandbox requests never share a handle.

Poll for the result

Poll with the jobId returned above:

GET /v1/booking/jobs/{jobId}

While the recommendation is still computing, the API returns 202:

{
"status": "processing"
}

The response includes Retry-After: 2; wait at least that many seconds before polling again.

When it completes, the API returns 200 with the ranked delivery windows:

{
"status": "complete",
"windows": [
{
"deliveryWindowStart": "2026-08-15T14:00:00.000Z",
"deliveryWindowEnd": "2026-08-15T16:00:00.000Z"
}
]
}

Each window exposes only the proposed delivery window. Windows are ordered best first.

If the search completes but no honorable scheduled window is available, the response still completes successfully with an empty windows array and a fallback signal indicating the caller should fall back to an on-demand ("rush") delivery:

{
"status": "complete",
"windows": [],
"fallback": "asap"
}

A jobId is only pollable by the account that created it, and results are retained for 10 minutes after completion.

Errors

Invalid input returns 400 invalid_input. An unknown branch returns 400 branch_not_found; an out-of-scope branch returns 403 branch_out_of_scope. If no usable pickup origin can be resolved, the API returns 400 origin_unresolved. If the destination postal code has no known timezone, the API returns 400 timezone_not_found. Rate-limited requests return 429 with both a Retry-After header and retryAfterSeconds in the error body. Slot-policy failures return 400 date_out_of_range, 400 date_blackout, 400 lead_time_not_met, 400 same_day_cutoff_passed, or 400 timeframe_bucket_not_available. Transient server failures return 500 input_resolution_failed or 500 enqueue_failed; retrying the same request is safe and idempotent.

When polling, an unknown, expired, or out-of-scope jobId returns 404 job_not_found, and a recommendation that failed to produce a usable result returns 500 optimization_failed (retrying is safe).