Getting started
Quick start
Credentials, a token and your first call, in ten minutes.
The API lets your own systems read and write one organisation’s data in Quarters. A website reads listings and the calendar and posts inquiries. A contract, accounting or reporting system reads reservations and their agreements. Both subscribe to webhooks instead of polling.
The base URL is https://go.quarters.live/api/v1.
1. Create credentials
In the dashboard, open Settings › Integrations and create an API credential. You
get a client ID (qc_…) and a client secret (qs_…). The secret is shown
once. If you lose it, create a new set.
2. Get a token
Exchange the credentials for an access token at the token endpoint. The token lasts 24 hours. Cache and re-use it, and mint a new one when a call answers 401.
Request
curl -X POST "https://go.quarters.live/api/v1/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=qc_YOUR_CLIENT_ID" \
-d "client_secret=qs_YOUR_CLIENT_SECRET"const res = await fetch("https://go.quarters.live/api/v1/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
"grant_type": "client_credentials",
"client_id": "qc_YOUR_CLIENT_ID",
"client_secret": "qs_YOUR_CLIENT_SECRET"
})
});
const data = await res.json();import requests
res = requests.post(
"https://go.quarters.live/api/v1/oauth2/token",
data={
"grant_type": "client_credentials",
"client_id": "qc_YOUR_CLIENT_ID",
"client_secret": "qs_YOUR_CLIENT_SECRET",
},
)
res.raise_for_status()
data = res.json()$ch = curl_init("https://go.quarters.live/api/v1/oauth2/token");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/x-www-form-urlencoded"],
CURLOPT_POSTFIELDS => http_build_query([
"grant_type" => "client_credentials",
"client_id" => "qc_YOUR_CLIENT_ID",
"client_secret" => "qs_YOUR_CLIENT_SECRET",
]),
]);
$res = curl_exec($ch);
curl_close($ch);
$data = json_decode($res, true);require "net/http"
require "json"
uri = URI("https://go.quarters.live/api/v1/oauth2/token")
req = Net::HTTP::Post.new(uri)
req.set_form_data({
"grant_type" => "client_credentials",
"client_id" => "qc_YOUR_CLIENT_ID",
"client_secret" => "qs_YOUR_CLIENT_SECRET",
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"strings"
)
func main() {
token := os.Getenv("ACCESS_TOKEN")
form := url.Values{
"grant_type": {"client_credentials"},
"client_id": {"qc_YOUR_CLIENT_ID"},
"client_secret": {"qs_YOUR_CLIENT_SECRET"},
}
req, _ := http.NewRequest("POST", "https://go.quarters.live/api/v1/oauth2/token", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
json.NewDecoder(res.Body).Decode(&data)
fmt.Println(data)
}Response · 200
{
"token_type": "Bearer",
"access_token": "qat_…",
"expires_in": 86400
}3. Make your first call
Send the token as Authorization: Bearer on every call. This lists your organisation’s published
listings, sorted by nickname:
Request
curl "https://go.quarters.live/api/v1/listings" \
-H "Authorization: Bearer $ACCESS_TOKEN"const res = await fetch("https://go.quarters.live/api/v1/listings", {
headers: { Authorization: `Bearer ${token}` }
});
const { listings } = await res.json();import requests
res = requests.get(
"https://go.quarters.live/api/v1/listings",
headers={"Authorization": f"Bearer {token}"},
)
res.raise_for_status()
listings = res.json()["listings"]$ch = curl_init("https://go.quarters.live/api/v1/listings");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"],
]);
$res = curl_exec($ch);
curl_close($ch);
$listings = json_decode($res, true)["listings"];require "net/http"
require "json"
uri = URI("https://go.quarters.live/api/v1/listings")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{token}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
listings = JSON.parse(res.body)["listings"]package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
token := os.Getenv("ACCESS_TOKEN")
req, _ := http.NewRequest("GET", "https://go.quarters.live/api/v1/listings", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
json.NewDecoder(res.Body).Decode(&data)
fmt.Println(data["listings"])
}An empty listings array means nothing is published yet. The API serves what Publish froze,
never what is being edited. Publish a property in the dashboard and call again.
Where to go next
- Conventions: money, dates, ids and caching.
- Webhooks: so you never have to poll.
- Build a booking website: listings, calendar and the form.
- Sync reservations: for contracts, accounting or a guest portal.
- The reference: every endpoint and every object.