API reference
All endpoints live under https://app.tikk.chat/api/v1, return JSON and require a Bearer token:
curl https://app.tikk.chat/api/v1/profile \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Accept: application/json"
Timestamps are ISO 8601. Everything is scoped to the Tikk user the token belongs to.
Token
GET /token works with any valid token and needs no scope.
Describes the token making the request: the scopes it holds, and whether each one works on the user's current plan.
{
"plan": "free",
"scopes": [
{ "id": "profile.read", "available": true },
{ "id": "bookings.read", "available": false, "reason": "plan_required" }
]
}
Check available rather than hard-coding which scopes need Pro. See Plans.
Profile
GET /profile requires profile.read.
Returns the user's public profile.
{
"username": "jane",
"name": "Jane Doe",
"title": "Coach",
"bio": "…",
"avatar_url": "https://…",
"logo_url": null,
"profile_url": "https://tikk.chat/jane",
"profile_complete": true,
"plan": "pro"
}
profile_complete is false until the user has picked a username; profile_url is null until then. plan is free or pro.
Services
GET /services requires profile.read.
The services visitors can book right now: active ones, within the user's plan limit. The default service comes first. Single-use links are not services; they're under Invites.
GET /services/{slug} requires profile.read.
A single service. A slug that isn't bookable right now returns 404.
{
"data": {
"slug": "intro-call",
"title": "Intro call",
"description": "A first chat to see if we're a fit.",
"is_default": true,
"is_group": false,
"max_attendees": null,
"requires_approval": true,
"durations": [
{ "minutes": 30, "price": null, "currency": "EUR" },
{ "minutes": 60, "price": 7500, "currency": "EUR" }
],
"location": { "type": "video", "label": "Video Meeting", "address": null },
"embed_url": "https://tikk.chat/jane/intro-call"
}
}
price is in minor units (cents), and null for a free duration. requires_approval is true when the user accepts each booking by hand. embed_url is null until the user has picked a username; see Embedding booking pages for what to do with it.
Availability
GET /availability requires availability.read.
Free slots per day, computed exactly as the user's public booking page does, including their working hours, buffers and busy calendars.
| Query parameter | Default | Notes |
|---|---|---|
service |
none | A service slug from /services. Unknown slugs return 404. |
from |
today | Date, e.g. 2026-10-01. |
to |
from + 7 days |
At most 31 days after from. |
duration |
the service's shortest duration, or 30 | Minutes, 15 to 180. |
{
"data": {
"2026-10-01": [
{ "start": "2026-10-01T08:00:00Z", "end": "2026-10-01T08:30:00Z", "label": "10:00" }
]
},
"meta": {
"from": "2026-10-01",
"to": "2026-10-08",
"duration_minutes": 30,
"timezone": "Europe/Brussels",
"service": "intro-call"
}
}
Days without free slots are left out. start and end are UTC; label is in the user's timezone. Group services also return spots_remaining per slot.
Bookings
GET /bookings requires bookings.read and a user on Tikk Pro.
The user's bookings, paginated.
| Query parameter | Default | Notes |
|---|---|---|
status |
all | pending, accepted, declined or cancelled. |
from, to |
none | Filter on the scheduled time. |
order |
desc |
asc or desc, by scheduled time. |
per_page |
25 | At most 100. |
The response has data (a list of bookings, shaped as below), plus links and meta for pagination.
GET /bookings/{id} requires bookings.read and a user on Tikk Pro.
A single booking. An id that doesn't belong to the user returns 404.
{
"id": 48213,
"status": "accepted",
"topic": "Intro call",
"duration_minutes": 30,
"scheduled_at": "2026-10-01T08:00:00+00:00",
"created_at": "2026-09-28T14:12:09+00:00",
"notes": null,
"cancellation_reason": null,
"booker": { "name": "Sam Smith", "email": "[email protected]" },
"attendees": [],
"service": {
"slug": "intro-call",
"title": "Intro call",
"is_group": false,
"max_attendees": null,
"is_personal_invite": false
},
"location": { "type": "google_meet", "label": "Google Meet", "address": null },
"meeting_link": null,
"conference_link": "https://meet.google.com/…",
"payment": { "status": null, "amount": null, "currency": "EUR" }
}
payment.amount is in minor units (cents).
Invites
Single-use links: a private booking link for one person, spent once it's booked. Every booking made through one is accepted straight away and is never paid.
GET /invites requires invites.read.
The user's links, newest first, paginated. Takes per_page (default 25, at most 100). The response has data plus links and meta for pagination.
GET /invites/{id} requires invites.read.
{
"data": {
"id": 73920,
"title": "Intro call for Sam",
"description": null,
"status": "open",
"url": "https://tikk.chat/jane/Xk3pQ9aB2c/intro-call-for-sam",
"contact": { "name": "Sam Smith", "email": "[email protected]" },
"durations": [30],
"expires_at": "2026-10-31T09:00:00+00:00",
"created_at": "2026-10-01T09:00:00+00:00"
}
}
status |
Meaning |
|---|---|
open |
Can be booked. |
used |
Booked; the link no longer works. |
expired |
Past expires_at without a booking. |
disabled |
Switched off by the user in Tikk. |
POST /invites requires invites.write.
Creates a link and returns it with status 201. Send the person its url.
| Field | Notes |
|---|---|
service |
Optional. A service slug to copy: the link offers that service's durations and location. |
title |
Required without service, at most 50 characters. With service it replaces the default title, "{service} for {contact_name}". |
description |
Optional, at most 500 characters. |
contact_name, contact_email |
Optional. Who the link is for; saved to the user's contacts when an email is given. |
expires_in_days |
1 to 365, default 30. |
duration |
Minutes, 5 to 480, default 30. Only without service. |
curl -X POST https://app.tikk.chat/api/v1/invites \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"service": "intro-call", "contact_name": "Sam Smith", "contact_email": "[email protected]"}'
DELETE /invites/{id} requires invites.write.
Deletes an unbooked link and returns 204. A link that has been booked returns 409 and stays: it's the booking's record of how it was made.