Skip to content
The Quarters

Developers

Four endpoints and a webhook.

Small enough to read in a sitting, which is deliberate. This is the surface your own website talks to, and it is the whole surface. There is nothing else to discover later.

Snapshots, not live rows

Listing content is frozen by the Publish button. Nothing external ever reads a property mid-edit.

Except availability

The calendar is computed on request. A booking taken seconds ago is already reflected.

Keys are per organisation

Issued and revoked in settings. Only a SHA-256 hash is stored, so we cannot show you a key twice.

Authentication

One header.

Create a key under Settings → Integrations and send it as a bearer token. A missing key returns 401 with a message telling you the header to send; a revoked one returns 401 as well.

curl https://app.thequarter.ai/api/v1/listings \
  -H "Authorization: Bearer qtr_live_…"

Every response is JSON. Errors carry a message field and a status that means what it says: 400 for a bad field, naming the field; 401 for the key; 404 for a listing that is unknown or unpublished.

Endpoints

All of them.

GET /api/v1/listings

Every published listing

The full set of published properties for the key’s organisation, served from publish snapshots. A property that has never been published does not appear, and content being edited right now does not leak.

Response

{
  "listings": [
    {
      "id": "prop_…",
      "slug": "jordaan-1a",
      "nickname": "JOR-1A",
      "active": true,
      "isListed": true,
      "propertyType": "apartment",
      "roomType": "entire_home",
      "address": {
        "street": "…", "postalCode": "1015 AA",
        "city": "Amsterdam", "country": "NL",
        "area": "Jordaan", "latitude": 52.37, "longitude": 4.88
      },
      "accommodates": 2, "bedrooms": 1, "beds": 1,
      "bathrooms": 1, "squareMeter": 58,
      "timezone": "Europe/Amsterdam",
      "pricing": {
        "currency": "EUR",
        "monthlyRateCents": 240000,
        "depositCents": 240000,
        "utilitiesCents": 15000,
        "furnitureCents": null
      },
      "terms": { "minNights": 30, "maxNights": 365 },
      "amenities": ["wifi", "washer", "…"],
      "description": { "title": "…", "summary": "…" },
      "images": [
        {
          "id": "img_…", "isPrimary": true, "caption": null,
          "sortOrder": 0, "width": 2400, "height": 1600, "bytes": 481221,
          "thumbnail": "https://…", "regular": "https://…",
          "large": "https://…", "original": "https://…"
        }
      ],
      "publishedAt": "2026-07-28T09:12:44.000Z"
    }
  ]
}
  • Sorted by nickname, so the order is stable between calls.
  • If you mirror this into your own database, treat the response as the COMPLETE set and delete rows it no longer contains. That is how an unpublished listing disappears from your site.
  • Image URLs are absolute. On local-disk storage they are made absolute against the request origin; on S3 they already are.
GET /api/v1/listings/{id}

One listing

The same object as above, for a single property. 404 if the id is unknown to your organisation or has never been published.

Response

{ "listing": { … same shape as above … } }
GET /api/v1/listings/{id}/calendar

Live availability

Per-day availability, computed on request from reservations and calendar blocks. This is the one endpoint that is NOT snapshot-bound: a booking taken thirty seconds ago is reflected here immediately, because a stale calendar double-books a nine-month tenancy.

FieldTypeNotes
startYYYY-MM-DDDefaults to today, Europe/Amsterdam.
endYYYY-MM-DDINCLUSIVE. Defaults to start + 730 days. The range may span at most 1100 days.

Response

{
  "days": [
    { "date": "2026-08-03", "status": "booked",      "minNights": 30, "isBaseMinNights": true },
    { "date": "2026-08-04", "status": "reserved",    "minNights": 30, "isBaseMinNights": true },
    { "date": "2026-08-05", "status": "unavailable", "minNights": 30, "isBaseMinNights": true },
    { "date": "2026-08-06", "status": "available",   "minNights": 30, "isBaseMinNights": true }
  ]
}
  • `booked` is a confirmed tenancy or later; `reserved` is held but not firm; `unavailable` is a calendar block (renovation, owner use); `available` is bookable.
  • Enquiries never block a day. Cancelling releases the days immediately.
  • Check-out is EXCLUSIVE. A tenancy ending on the 30th leaves the 30th available, because the handover is at noon that day.
  • The listing must be published. An unpublished property returns 404 here too.
POST /api/v1/inquiries

Your booking form lands here

Turns a website enquiry into a row staff convert to a reservation in one click, resolved to a guest record, rather than into an email somebody re-types. Anything your form collects beyond the fields below travels in `metadata` untyped, so the form can evolve without a contract change.

FieldTypeNotes
lastNamestring, requiredMax 100 characters.
emailstring, requiredMust be a valid address. Max 200.
firstName / phonestringOptional.
propertyIdstringOptional. If given it must be one of yours, so a typo fails loudly rather than creating an orphan.
checkIn / checkOutYYYY-MM-DDOptional. checkOut must be after checkIn.
adults / children / infantsinteger 0–20The tenants picker, one field per age band.
petsbooleanOptional.
messagestringMax 10,000 characters.
localestringWhich language they were reading. Max 10.
sourcestringDefaults to "website".
metadataobjectAnything else. Max 10 KB serialised.

Response

201 Created

{ "id": "inq_…" }
  • `guestCount` is accepted and folded into `adults`. It is deprecated; send the age bands.
  • A bad field returns 400 with a message naming the path, so a broken form is debuggable from the response.

Webhooks

So you do not have to poll.

Register an endpoint in settings and we POST to it when something changes. Each delivery carries a signature computed from a secret only you and we hold, so your endpoint can prove the call came from us before acting on it.

Deliveries are logged with their response. A failure is visible and retryable rather than silently lost, which is the difference between a webhook you trust and one you back up with a nightly poll anyway.

  • listing.updated

    A property was published, or re-published with changes.

  • listing.deleted

    A property was unpublished or removed. Drop it from your site.

  • calendar.updated

    Availability changed for a property: a booking, a cancellation, a block.

  • ping

    You pressed the test button in settings. Confirms the endpoint and the signature.

Verify: HMAC-SHA256 of the raw body, keyed with your endpoint secret.

Conventions

Four things that will bite you otherwise.

  • Money is integer euro cents

    €2,400.00 is 240000. There are no floats anywhere in this API, and your side should not introduce one.

  • Dates are YYYY-MM-DD strings, not timestamps

    A tenancy date is a calendar day in Europe/Amsterdam, not an instant. Parsing one into a UTC datetime is how a tenancy moves by a day for half the year.

  • Check-out is exclusive

    A tenancy from the 1st to the 30th occupies the 1st through the 29th. The 30th is available; the handover is at noon.

  • The listings response is the complete set

    Not a delta and not paginated. Rows that vanish have been unpublished, and your mirror should delete them.

This page was checked against the running implementation on 3 August 2026.

Building against it?

Write to us before you start rather than after. We’d rather change the API than watch you work around it.

30 days free · no card required · cancel by closing the tab