Skip to content
Keptslot
Start free

Get a key and make your first call

This guide books one appointment with curl. It takes six steps. The base URL is https://api.keptslot.com.

Read the glossary first if you have not. One noun here does not mean what the dashboard calls it: staff is a bookable calendar, not a person with a login.

  1. Open the dashboard and select your workspace.
  2. Go to Developer → API keys.
  3. Create a key of type Secret.

CAUTION: The full key value is shown once. Store it now. A later read gives null, and no support call can recover it. If you lose the key, revoke it and create another one.

A secret key starts with sk_. Keep it on your server. Do not put it in a browser. For the difference between the key types, read the keys guide.

Every call sends the key as a bearer token.

Terminal window
curl https://api.keptslot.com/v1/workspace \
-H "Authorization: Bearer sk_4bT1nQ7xLd9sVzK0pR2eYw8hC6jM3gF5uA1oS7iN0dE"

A 200 returns your workspace settings:

{
"name": "Northside Barbers",
"timezone": "Europe/London",
"currency": "GBP",
"slot_interval_minutes": 15,
"require_booking_verification": false
}

If the response is a 401, the key is wrong or revoked. If the response is a 403 with secret_key_required, you sent a publishable key.

Note the timezone. Every time in this API is an RFC3339 timestamp, and the responses use the timezone of the workspace. The appointment happens at the business, so the offset is the business’s and not the customer’s.

A booking is always for exactly one service. Get the service id first.

Terminal window
curl https://api.keptslot.com/v1/services \
-H "Authorization: Bearer sk_4bT1nQ7xLd9sVzK0pR2eYw8hC6jM3gF5uA1oS7iN0dE"
{
"data": [
{
"id": "3a7b1e90-2c4d-4f81-9b02-7e5c1a6d8f44",
"name": "Cut and finish",
"duration_minutes": 45,
"price_cents": 4000,
"active": true
}
]
}

This endpoint returns inactive services too. Read active to tell them apart. A customer cannot book an inactive service.

Ask for one service and one date range. The range must be 62 days or less.

Terminal window
curl -G https://api.keptslot.com/v1/availability \
-H "Authorization: Bearer sk_4bT1nQ7xLd9sVzK0pR2eYw8hC6jM3gF5uA1oS7iN0dE" \
--data-urlencode "service_id=3a7b1e90-2c4d-4f81-9b02-7e5c1a6d8f44" \
--data-urlencode "from=2026-09-01T00:00:00+01:00" \
--data-urlencode "to=2026-09-08T00:00:00+01:00"

The slots are grouped by calendar:

{
"data": [
{
"staff_id": "6f1c0b52-9a3e-4f77-8d21-0b5a2c9e4d10",
"slots": [
{ "start": "2026-09-01T09:00:00+01:00", "end": "2026-09-01T09:30:00+01:00" },
{ "start": "2026-09-01T09:30:00+01:00", "end": "2026-09-01T10:00:00+01:00" }
]
}
]
}

An empty data array means the range has no open slots. It is not an error.

Send one slot start from step 4. If you omit staff_id, the API picks an open calendar for you.

Terminal window
curl -X POST https://api.keptslot.com/v1/bookings \
-H "Authorization: Bearer sk_4bT1nQ7xLd9sVzK0pR2eYw8hC6jM3gF5uA1oS7iN0dE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
-d '{
"service_id": "3a7b1e90-2c4d-4f81-9b02-7e5c1a6d8f44",
"starts_at": "2026-09-01T09:00:00+01:00",
"customer": {
"name": "Sam Okafor",
"email": "sam@example.com",
"phone": "+447700900123"
}
}'
{
"id": "b41d9f2a-6c58-4a13-9d77-2e8f0c3b5a91",
"staff_id": "6f1c0b52-9a3e-4f77-8d21-0b5a2c9e4d10",
"service_id": "3a7b1e90-2c4d-4f81-9b02-7e5c1a6d8f44",
"customer_id": "d2f4a6c8-1b3e-4d5f-8a70-9c1e2b4d6f80",
"starts_at": "2026-09-01T09:00:00+01:00",
"ends_at": "2026-09-01T09:45:00+01:00",
"status": "confirmed",
"access_token": "kQ8xR2vN7pL0aY4tB6cW1zJ5hM3sD9fG2uE7iO0nX4k"
}

Two fields decide what happens next.

access_token is on this response and on no other. It is the customer’s manage link. Store it now.

status is confirmed or pending. If the status is confirmed, you are done. If the status is pending, go to step 6.

Send an Idempotency-Key on every create, as the sample does. A retry with the same key and the same body returns the stored response instead of a second booking. Read the rate limits guide for what a repeat returns.

A pending booking holds the slot and is not confirmed. The customer gets a 6-digit code by email. Send that code back.

Terminal window
curl -X POST https://api.keptslot.com/v1/bookings/b41d9f2a-6c58-4a13-9d77-2e8f0c3b5a91/verify \
-H "Authorization: Bearer sk_4bT1nQ7xLd9sVzK0pR2eYw8hC6jM3gF5uA1oS7iN0dE" \
-H "Content-Type: application/json" \
-d '{ "code": "482915" }'
{ "status": "confirmed" }

Every refusal here returns the same error. A wrong code, an expired hold, too many attempts and an unknown booking are not told apart. The response is deliberately not an oracle for a 6-digit code.

Decide this step from the status on the create response. Do not decide it from require_verification on the bootstrap response. A business can change that setting at any time.