Introduction
About TimelyDo
TimelyDo is a scheduling platform. You publish a booking page, share the link, and people pick a time from your real availability. No back and forth emails, and no double bookings.
TimelyDo syncs with Google Calendar, so events already on your calendar block those time slots. It connects to Zoom and Google Meet for the meetings people book with you. Accounts are personal, and organizations can group members together.
Using TimelyDo is free. Learn more on the features pages or in the Help Center.
About the API
The TimelyDo API lets you work with your TimelyDo account from your own applications, scripts and integrations. It uses the same data you see in the app, so anything you read or change through the API shows up in TimelyDo straight away.
Need help? Email support@timelydo.com.
Every example on this page comes in Shell (curl), Ruby, Python and JavaScript. Switch languages with the tabs at the top right.
Base URL
All endpoints live under:
https://timelydo.com/api/v1
Response format
Every response, success or failure, has the same shape:
{
"success": true,
"message": "user details",
"data": {},
"status": 200
}
Requests and responses are JSON. Every response carries the same four fields:
| Field | Type | Description |
|---|---|---|
| success | boolean | true when the request worked, false otherwise. |
| message | string | A short human readable description of the result. |
| data | object or array | The payload. An empty object when there is nothing to return. |
| status | integer or string | Mirrors the HTTP status. Successful responses use the number (200). Errors use the status name ("unauthorized", "forbidden"). |
Always check the HTTP status code or success before reading data.
Authentication
The TimelyDo API uses API keys. Every request must send your key in the X-API-KEY header. Requests without a valid key are rejected with 401 Unauthorized.
Get your API key
Every TimelyDo account has one API key, created when the account is created. To find it:
- Sign in at timelydo.com.
- Open Settings, then Developers Console, then API Key. You can also go straight to timelydo.com/settings/developers/api_key.
- Click the copy button next to Your API Key.
Keys are 73 characters long. They do not expire. A key keeps working until you regenerate it or delete your account.
The examples on this page read the key from an environment variable called TIMELYDO_API_KEY, so it never appears in your code:
export TIMELYDO_API_KEY="paste your key here"
Authenticate your requests
curl "https://timelydo.com/api/v1/user" \
-H "X-API-KEY: $TIMELYDO_API_KEY" \
-H "Accept: application/json"
require 'net/http'
require 'json'
uri = URI('https://timelydo.com/api/v1/user')
request = Net::HTTP::Get.new(uri)
request['X-API-KEY'] = ENV.fetch('TIMELYDO_API_KEY')
request['Accept'] = 'application/json'
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
body = JSON.parse(response.body)
if body['success']
puts "Authenticated as #{body['data']['email']}"
else
warn "#{response.code}: #{body['message']}"
end
import os
import requests
response = requests.get(
"https://timelydo.com/api/v1/user",
headers={
"X-API-KEY": os.environ["TIMELYDO_API_KEY"],
"Accept": "application/json",
},
timeout=10,
)
body = response.json()
if body["success"]:
print("Authenticated as", body["data"]["email"])
else:
print(f"{response.status_code}: {body['message']}")
// Node.js 18 or newer (built in fetch). Run this on your server, never in a browser.
async function getCurrentUser() {
const response = await fetch("https://timelydo.com/api/v1/user", {
headers: {
"X-API-KEY": process.env.TIMELYDO_API_KEY,
"Accept": "application/json",
},
});
const body = await response.json();
if (body.success) {
console.log(`Authenticated as ${body.data.email}`);
} else {
console.error(`${response.status}: ${body.message}`);
}
}
getCurrentUser();
A valid key returns the account it belongs to (trimmed):
{
"success": true,
"message": "user details",
"data": {
"id": "3f6c2a9e-8b1d-4c7a-9e21-5d0b7f4a1c33",
"email": "jane@example.com",
"full_name": "Jane Doe",
"url": "jane",
"avatar_name": "JD",
"timezone": "Berlin",
"date_timezone": "Europe/Berlin",
"locale": "en",
"time_format": "12_hours",
"date_format": "31/12/2024",
"currency": "eur",
"country_iso_code": "de",
"api_key": "YOUR_API_KEY",
"created_at": "2026-08-09T04:23:16.260Z"
},
"status": 200
}
A missing or wrong key returns
401:
{
"success": false,
"message": "Invalid api_key",
"data": {},
"status": "unauthorized"
}
Send the key in the X-API-KEY header on every request:
X-API-KEY: YOUR_API_KEY
The header name is not case sensitive. The key itself must match exactly.
The quickest way to check a key is GET /api/v1/user. It returns the account the key belongs to.
HTTP Request
GET https://timelydo.com/api/v1/user
Authentication errors
| HTTP status | message | Cause |
|---|---|---|
| 401 | api_key is missing |
No X-API-KEY header was sent. |
| 401 | Invalid api_key |
The key does not match any active account. It may have been regenerated, or the account was deleted. |
| 403 | depends on the endpoint | The endpoint only works from a signed in browser session. See below. |
Endpoints that need a signed in session
A few account security settings cannot be changed with an API key, even a valid one. This protects your account if a key ever leaks. These endpoints return 403 Forbidden for API key requests:
| Endpoint | 403 message |
|---|---|
/api/v1/two_factor (all methods) and /api/v1/two_factor/backup_codes |
Sign in to the app to manage two factor authentication. |
/api/v1/hellobar and its actions |
A signed in session is required |
Manage these from the TimelyDo app instead.
Regenerate your API key
Regenerate your key if it may have leaked, or to rotate it on a schedule. The old key stops working immediately, so update every integration that uses it right away.
You can regenerate the key in two ways.
In the app
- Open Settings, then Developers Console, then API Key.
- Click Regenerate key.
- Copy the new key shown on the page.
With the API
curl -X PUT "https://timelydo.com/api/v1/users/regenerate_api_key" \
-H "X-API-KEY: $TIMELYDO_API_KEY" \
-H "Accept: application/json"
require 'net/http'
require 'json'
uri = URI('https://timelydo.com/api/v1/users/regenerate_api_key')
request = Net::HTTP::Put.new(uri)
request['X-API-KEY'] = ENV.fetch('TIMELYDO_API_KEY')
request['Accept'] = 'application/json'
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
body = JSON.parse(response.body)
if body['success']
new_api_key = body['data']['api_key']
# Save new_api_key somewhere safe now. The old key has already stopped working.
puts "New key received (#{new_api_key.length} characters)"
else
warn "#{response.code}: #{body['message']}"
end
import os
import requests
response = requests.put(
"https://timelydo.com/api/v1/users/regenerate_api_key",
headers={
"X-API-KEY": os.environ["TIMELYDO_API_KEY"],
"Accept": "application/json",
},
timeout=10,
)
body = response.json()
if body["success"]:
new_api_key = body["data"]["api_key"]
# Save new_api_key somewhere safe now. The old key has already stopped working.
print(f"New key received ({len(new_api_key)} characters)")
else:
print(f"{response.status_code}: {body['message']}")
// Node.js 18 or newer (built in fetch). Run this on your server, never in a browser.
async function regenerateApiKey() {
const response = await fetch("https://timelydo.com/api/v1/users/regenerate_api_key", {
method: "PUT",
headers: {
"X-API-KEY": process.env.TIMELYDO_API_KEY,
"Accept": "application/json",
},
});
const body = await response.json();
if (body.success) {
const newApiKey = body.data.api_key;
// Save newApiKey somewhere safe now. The old key has already stopped working.
console.log(`New key received (${newApiKey.length} characters)`);
} else {
console.error(`${response.status}: ${body.message}`);
}
}
regenerateApiKey();
The response contains the new key:
{
"success": true,
"message": "api_key is updated",
"data": {
"api_key": "YOUR_NEW_API_KEY"
},
"status": 200
}
Authenticate this request with your current key. The response returns the new key, and from that moment only the new key works.
HTTP Request
PUT https://timelydo.com/api/v1/users/regenerate_api_key
This endpoint takes no parameters.
If you lost your key
You cannot get a key back through the API without a working key. Sign in to TimelyDo and copy it from the API Key page, or regenerate a new one there.
Errors
Errors use the same response shape as successful requests:
{
"success": false,
"message": "missing params",
"data": {
"user": ["profile_picture"]
},
"status": "bad_request"
}
When a request fails, success is false, message says what went wrong, and status holds the HTTP status name. For some errors data carries extra detail, such as the list of missing fields.
| HTTP status | status field | Meaning |
|---|---|---|
| 400 | bad_request |
Required parameters are missing. data lists them, grouped by object. |
| 401 | unauthorized |
The X-API-KEY header is missing, or the key is not valid. See Authentication. |
| 403 | forbidden |
Your key is valid, but this endpoint only works from a signed in browser session. |
| 404 | not_found |
The endpoint does not exist (endpoint does not exists), or the record you asked for was not found. |
| 422 | unprocessable_entity |
The request was understood, but the values failed validation. message explains which ones. |
| 500 | internal_server_error |
Something went wrong on our side. Try again later. If it keeps happening, contact support. |