Guides
Code examples
Copy-paste clients for curl, JavaScript, Python, PHP and C#.
All examples read QUILO_URL and QUILO_API_KEY from the environment.
curl#
Terminal
# read
curl "$QUILO_URL/api/v1/appointments?starts_after=2026-10-20T00:00:00Z&limit=20" \
-H "Authorization: Bearer $QUILO_API_KEY"
# create
curl -X POST "$QUILO_URL/api/v1/appointments" \
-H "Authorization: Bearer $QUILO_API_KEY" -H "Content-Type: application/json" \
-d '{"brand_id":"0b7e9a52-…","title":"Ring fitting","starts_at":"2026-10-20T14:00:00+02:00","ends_at":"2026-10-20T14:45:00+02:00"}'
# change
curl -X PATCH "$QUILO_URL/api/v1/tasks/TASK_ID" \
-H "Authorization: Bearer $QUILO_API_KEY" -H "Content-Type: application/json" \
-d '{"status":"done"}'JavaScript / TypeScript (Node 18+)#
TypeScript
type QuiloError = { error: { type: string; message: string; request_id: string; errors?: { field: string; message: string }[] } };
export class Quilo {
constructor(private base = process.env.QUILO_URL!, private key = process.env.QUILO_API_KEY!) {}
async request<T>(method: string, path: string, body?: unknown, idempotencyKey?: string): Promise<T> {
for (let attempt = 0; ; attempt++) {
const res = await fetch(`${this.base}/api/v1${path}`, {
method,
headers: {
Authorization: `Bearer ${this.key}`,
...(body ? { 'Content-Type': 'application/json' } : {}),
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
if ((res.status === 429 || res.status >= 500) && attempt < 4) {
await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get('retry-after') ?? 2 ** attempt)));
continue;
}
const json = await res.json();
if (!res.ok) throw new Error(`${(json as QuiloError).error.type}: ${(json as QuiloError).error.message} (${(json as QuiloError).error.request_id})`);
return json as T;
}
}
get = <T>(path: string) => this.request<T>('GET', path);
create = <T>(resource: string, body: unknown, idem?: string) => this.request<T>('POST', `/${resource}`, body, idem);
update = <T>(resource: string, id: string, body: unknown) => this.request<T>('PATCH', `/${resource}/${id}`, body);
remove = (resource: string, id: string) => this.request('DELETE', `/${resource}/${id}`);
}
// const q = new Quilo();
// const me = await q.get('/me');
// const task = await q.create('tasks', { brand_id, title: 'Call Elena' }, 'task-elena-1');Python 3#
Python
import os, time, requests
class Quilo:
def __init__(self, base=os.environ.get("QUILO_URL"), key=os.environ.get("QUILO_API_KEY")):
self.base = f"{base}/api/v1"
self.s = requests.Session()
self.s.headers["Authorization"] = f"Bearer {key}"
def request(self, method, path, json=None, idempotency_key=None, params=None):
headers = {"Idempotency-Key": idempotency_key} if idempotency_key else {}
for attempt in range(5):
r = self.s.request(method, self.base + path, json=json, params=params, headers=headers, timeout=30)
if r.status_code in (429, 500, 502, 503, 504) and attempt < 4:
time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
continue
if r.status_code >= 400:
e = r.json()["error"]
raise RuntimeError(f"{e['type']}: {e['message']} ({e['request_id']})")
return r.json()
def list_all(self, resource, **filters):
cursor = None
while True:
page = self.request("GET", f"/{resource}", params={**filters, "limit": 200, **({"cursor": cursor} if cursor else {})})
yield from page["data"]
if not page["has_more"]:
return
cursor = page["next_cursor"]
# q = Quilo()
# for c in q.list_all("clients", updated_since="2026-10-01T00:00:00Z"): print(c["email"])PHP#
See the helper in the website form guide.
C# (.NET 8)#
C#
using System.Net.Http.Headers;
using System.Net.Http.Json;
var http = new HttpClient { BaseAddress = new Uri(Environment.GetEnvironmentVariable("QUILO_URL") + "/api/v1/") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("QUILO_API_KEY"));
var me = await http.GetFromJsonAsync<System.Text.Json.JsonElement>("me");
var req = new HttpRequestMessage(HttpMethod.Post, "tasks") { Content = JsonContent.Create(new { brand_id = "0b7e9a52-…", title = "Call Elena" }) };
req.Headers.Add("Idempotency-Key", "task-elena-1");
var res = await http.SendAsync(req);
Console.WriteLine($"{(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");OpenAPI#
The machine-readable description is at $QUILO_URL/api/v1/openapi.json (no key needed). Import it into Postman, Insomnia, Bruno or an OpenAPI code generator to get a typed client in your language.