Skip to main content

Rental Agreements Overview

A rental agreement is the supplier's own signable document for one booking: their terms, with that booking's business details, customer details, line items, dates, total and security deposit already filled in.

There is at most one agreement per booking. It is created from the supplier's template the first time it is read, sent or signed โ€” so GET /v1/bookings/{id}/agreement is safe to call on a booking that has never had one.

GET    /v1/bookings/{id}/agreement
GET /v1/bookings/{id}/agreement/pdf
PATCH /v1/bookings/{id}/agreement
POST /v1/bookings/{id}/agreement/send
POST /v1/bookings/{id}/agreement/sign

Scopesโ€‹

ScopeGrants
agreements:readView a booking's agreement and download its PDF
agreements:writeEdit the terms, send the signing link, record a signature

Supplier gateโ€‹

Rental agreements are opt-in per business (Settings โ†’ Rental agreement). Every endpoint returns 403 with code agreements_disabled while the feature is switched off:

{
"error": {
"code": "agreements_disabled",
"message": "Rental agreements are switched off for this business. Turn them on in Settings โ†’ Rental agreement."
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-08-14T09:00:00+00:00"
}
}

Only a booking with rental items has an agreement. On an experience-only or store-only booking, every endpoint returns 409 with code agreement_not_applicable, unless the booking already carries an agreement.

The agreement resourceโ€‹

{
"booking_id": 4821,
"title": "Rental Agreement",
"status": "sent",
"status_label": "Waiting for signature",
"signing_url": "https://app.equipdash.com/agreement/AbCโ€ฆ",
"sent_at": "2026-08-14T09:00:00+00:00",
"send_count": 1,
"viewed_at": "2026-08-14T09:14:22+00:00",
"signed_at": null,
"signed_name": null,
"signed_ip": null,
"signed_via": null,
"terms": "This agreement is between โ€ฆ"
}
FieldTypeNotes
statusstringdraft (never sent), sent, or signed
status_labelstringThe same state in the supplier's own words
signing_urlstring|nullThe customer's private signing page. null once signed, and null for a key that does not hold agreements:write
send_countintegerHow many times the link has been emailed
viewed_atstring|nullFirst time the customer opened the link
signed_viastring|nullin_person, email_link, or api
termsstringThe wording as it stands for this booking
Treat signing_url as a bearer token

Anyone holding the link can sign the agreement. Don't log it, don't put it in an analytics payload, and don't forward it to anyone but the customer.

Because the link is the whole authorization to sign, it is only returned to a key that holds agreements:write. A key with agreements:read alone gets the full agreement โ€” status, dates, signature details, terms โ€” with signing_url set to null.

Lifecycleโ€‹

  1. Read the agreement โ€” created from the template on first read, with the merge tags already resolved.
  2. Edit the terms for this one booking if you need to (PATCH). The supplier's template is untouched.
  3. Send the signing link to the customer, or sign it directly from a device at the counter.
  4. Download the signed PDF whenever you need the record.

Once an agreement is signed it is frozen: the terms, the priced snapshot and the PDF never change again, and a second PATCH or sign returns 422.

Same engine as the portalโ€‹

These endpoints are thin wrappers over the same service the supplier portal and the AI assistant use, so an agreement signed through the API is byte-for-byte the same record โ€” same PDF, same audit trail โ€” as one signed at the counter.