Skip to main content

Quotes

A quote is a priced proposal built from the same line items as a booking β€” products, bundles, experiences, store products β€” that the customer reviews and accepts (or declines) through a private link. Accepting converts the quote into a normal confirmed booking at the quoted total.

Quote pricing runs through the exact same engine as booking pricing, so a quote's totals always match what the equivalent booking would cost.

Endpoints​

MethodEndpointDescription
GET/v1/quotesList quotes
GET/v1/quotes/{id}Get a single quote
POST/v1/quotesCreate a quote
PATCH/v1/quotes/{id}Update a quote
DELETE/v1/quotes/{id}Decline (archive) a quote
POST/v1/quotes/{id}/sendSend the quote to the customer
POST/v1/quotes/{id}/acceptAccept a quote
POST/v1/quotes/{id}/declineDecline a quote
POST/v1/quotes/{id}/duplicateDuplicate a quote

Scopes​

ScopeGrants
quotes:readView quotes
quotes:writeCreate, send and decide quotes

Quote Lifecycle​

A quote's state is derived from its timestamps β€” it is never set directly:

StateMeaning
draftCreated but not sent yet
sentEmailed to the customer, not opened yet
viewedThe customer has opened the quote page
acceptedConverted into a confirmed booking
declinedDeclined by the customer or on their behalf
expiredIts valid_until date passed before a decision
cancelledAccepted, then the booking it became was cancelled
draft ──send──▢ sent ──customer opens──▢ viewed
β”‚ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€accept───────────────▢ accepted (now a booking)
β”œβ”€β”€β”€β”€β”€β”€β”€β”€decline──────────────▢ declined
└────valid_until passes────┴──▢ expired

accepted ──booking cancelled──▢ cancelled

Key Concepts​

Holding dates (reserves_dates)​

A quote can optionally hold its dates (quote_reserves: true on create). While held, the quoted items and spots are blocked from other bookings until the customer decides. Held dates are released automatically when the quote is declined or expires.

A quote that does not hold its dates leaves availability open β€” if the dates are taken before acceptance, Accept returns 409 dates_unavailable and nothing is booked.

Expiry (valid_until)​

quote_valid_until is optional. A quote with valid_until: null never expires. A dated quote lapses automatically once the date passes (in the supplier's timezone): its state becomes expired, any held dates are released, and the supplier is notified. An optional customer reminder email can be configured a set number of days before expiry (Settings β†’ Quotes in the portal).

Agreements​

A quote can carry several agreements β€” published waiver/form documents (quote_agreement_form_ids, an array of form IDs) β€” which the customer reads and signs on the quote page before accepting. Each agreement gets its own signing link on the customer page, and acceptance stays blocked until every agreement is signed. The quote resource reports them under agreements (one entry per form with signed and signed_at); the legacy single-form agreement field mirrors the first attachment for older integrations.

Each quote gets a private, tokenized customer page (no login required). The link is minted on first send β€” until then customer_url is null. Reads never mint a link.

Quotes are never hard-deleted​

DELETE /v1/quotes/{id} declines the quote rather than deleting it β€” decided quotes stay on record. There is no way to hard-delete a quote via the API.

The Quote Object​

{
"id": 123,
"quote_number": "Q-123",
"state": "sent",
"reserves_dates": false,
"valid_until": "2026-03-01",
"sent_at": "2026-02-01T10:05:00+00:00",
"viewed_at": null,
"decided_at": null,
"decline_reason": null,
"agreements": [
{
"form_id": 12,
"form_name": "Rental Agreement",
"signed": false,
"signed_at": null
},
{
"form_id": 15,
"form_name": "Liability Agreement",
"signed": true,
"signed_at": "2026-02-02T15:12:00+00:00"
}
],
"agreement": {
"form_id": 12,
"form_name": "Rental Agreement",
"signed": false
},
"customer_url": "https://app.equipdash.com/quote/AbC123dEf456...",
"type": "rental",
"start_date": "2026-02-15T09:00:00+00:00",
"end_date": "2026-02-17T17:00:00+00:00",
"currency": "USD",
"pricing": {
"subtotal": 205.00,
"total": 225.50
},
"customer": {
"id": 456,
"first_name": "Jane",
"last_name": "Doe",
"full_name": "Jane Doe",
"is_organization": false,
"organization_name": null,
"display_name": "Jane Doe",
"email": "jane@example.com",
"phone": "+1234567890",
"phone_country_code": "+1",
"bookings_count": 3,
"created_at": "2025-06-01T09:00:00+00:00"
},
"products": [
{
"id": 45,
"product_id": 1,
"product_name": "Mountain Bike",
"variation_id": null,
"variation_name": null,
"bundle_in_booking_id": null,
"quantity": 2,
"price": 120.00
}
],
"experiences": [],
"bundles": [],
"store_products": [],
"created_at": "2026-02-01T10:00:00+00:00",
"updated_at": "2026-02-01T10:05:00+00:00"
}

Quote Fields​

FieldTypeDescription
idintegerUnique quote identifier
quote_numberstringHuman-readable number, e.g. "Q-123"
statestringDerived lifecycle state (see Quote Lifecycle)
reserves_datesbooleanWhether the quote holds its dates while the customer decides
valid_untilstring|nullExpiry date (YYYY-MM-DD). null = the quote never expires
sent_atstring|nullWhen the quote was last sent (ISO 8601)
viewed_atstring|nullWhen the customer first opened the quote page (ISO 8601)
decided_atstring|nullWhen the quote was accepted, declined, or expired (ISO 8601)
decline_reasonstring|nullThe decline reason. Empty string when declined without a reason; null while undecided
agreementsarrayEvery attached agreement: form_id, form_name, signed (boolean), signed_at (ISO 8601|null). Empty array when none are attached
agreementobject|nullLegacy single-agreement field β€” mirrors the FIRST attachment (form_id, form_name, signed). null when no agreements are attached
customer_urlstring|nullThe customer's private quote link. null until the quote is first sent
typestringBooking type of the underlying cart (rental, experience, store, service, mixed)
start_datestring|nullStart date/time (ISO 8601)
end_datestring|nullEnd date/time (ISO 8601)
currencystringCurrency code (ISO 4217)
pricingobjectsubtotal and total (tax-inclusive)
customerobjectCustomer summary
productsarrayRental product lines: id, product_id, product_name, variation_id, variation_name, bundle_in_booking_id, quantity, price
experiencesarrayExperience lines: id, experience_id, experience_name, schedule_id, rate_category_id, bundle_in_booking_id, quantity, booked_participants, price
bundlesarrayBundle lines: id, bundle_id, bundle_name, quantity, price
store_productsarrayStore product lines: id, store_product_id, product_name, variation_id, quantity, unit_price, total_price
created_atstringCreation timestamp (ISO 8601)
updated_atstringLast update timestamp (ISO 8601)

Typical Workflow​

  1. Create the quote with the same line-item payload as a booking: POST /v1/quotes
  2. Send it to the customer: POST /v1/quotes/{id}/send
  3. Track it: GET /v1/quotes/{id} β€” watch state, viewed_at, and the agreements[].signed flags
  4. Decide it (if the customer confirms out-of-band): POST /v1/quotes/{id}/accept or /decline
  5. Re-quote when dates change or a quote lapses: POST /v1/quotes/{id}/duplicate