Skip to content

Submit a booking (pending reservation)

POST
/bookings
curl --request POST \
--url https://demo.yourapp.com/api/v1/bookings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: example' \
--data '{ "quoteId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "customer": { "firstName": "example", "lastName": "example", "email": "hello@example.com", "phone": "example" }, "pickupLocation": "example", "dropoffLocation": "example", "notes": "example" }'

Creates a pending reservation from an unexpired quote. Staff confirm or reject it in the dashboard; poll GET /bookings/{id}.

Always send a unique Idempotency-Key per booking attempt. Retries with the same key replay the original response — success or business rejection alike — instead of creating a duplicate. A quote can be used exactly once (enforced by a database constraint, so concurrent submissions cannot double-book a quote). Requires bookings:create.

X-Session-Token
string

Required when the request carries an Origin header (browser channel)

Idempotency-Key
required
string
>= 1 characters <= 255 characters

Unique value per booking attempt (e.g. a UUID)

Media typeapplication/json
object
quoteId
required
string format: uuid
customer
required
object
firstName
required
string
<= 100 characters
lastName
required
string
<= 100 characters
email
string format: email
phone
string
<= 50 characters
pickupLocation
string
<= 500 characters
dropoffLocation
string
<= 500 characters
notes
string
<= 2000 characters
Examplegenerated
{
"quoteId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"customer": {
"firstName": "example",
"lastName": "example",
"email": "hello@example.com",
"phone": "example"
},
"pickupLocation": "example",
"dropoffLocation": "example",
"notes": "example"
}

Booking accepted for staff review

Media typeapplication/json
object
data
object
bookingId
required
string format: uuid
status
required

Pending until staff confirm or reject

string
Allowed values: pending confirmed converted cancelled no_show
assetGroupId
string | null format: uuid
pickupDatetime
required
string format: date-time
dropoffDatetime
required
string format: date-time
estimatedTotal
string | null
currency
string | null
createdAt
required
string format: date-time
Example
{
"data": {
"status": "pending"
}
}

Request validation failed (VALIDATION_ERROR)

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details
Array<object>
object
path
string
message
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"path": "example",
"message": "example"
}
]
}
}

Missing/invalid/revoked/expired API key (API_KEY_REQUIRED, API_KEY_INVALID, API_KEY_REVOKED, API_KEY_EXPIRED) or missing/invalid session token on the browser channel (SESSION_REQUIRED, SESSION_INVALID, SESSION_EXPIRED).

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details
Array<object>
object
path
string
message
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"path": "example",
"message": "example"
}
]
}
}

Quote belongs to a different session (QUOTE_SESSION_MISMATCH)

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details
Array<object>
object
path
string
message
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"path": "example",
"message": "example"
}
]
}
}

Quote not found for this key (QUOTE_NOT_FOUND)

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details
Array<object>
object
path
string
message
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"path": "example",
"message": "example"
}
]
}
}

QUOTE_EXPIRED — request a new quote. QUOTE_ALREADY_USED — a booking already exists for this quote. REQUEST_IN_PROGRESS — same Idempotency-Key currently processing. If the original request failed before a response was recorded (e.g. a 5xx), the key becomes retryable within ~2 minutes.

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details
Array<object>
object
path
string
message
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"path": "example",
"message": "example"
}
]
}
}

Rate limit exceeded (RATE_LIMITED)

Media typeapplication/json
object
error
required
object
code
required
string
message
required
string
details
Array<object>
object
path
string
message
string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"path": "example",
"message": "example"
}
]
}
}
Retry-After
integer

Seconds until the window resets

X-RateLimit-Limit
integer
X-RateLimit-Remaining
integer
X-RateLimit-Reset
integer

Unix timestamp (seconds) of the window reset