# API reference – Tiempo

> How the Tiempo API works: authentication, acting for a business, test and live modes, requests, versions, errors and webhooks, then every endpoint by topic.

Source: https://tiempo.one/docs

The Tiempo API answers “when can this be booked?” and places bookings safely for every business on your platform. It’s organised around objects with predictable URLs, takes and returns JSON, and uses standard HTTP status codes.

Tiempo owns time and its state. Your platform keeps services, prices, payments, customers and messages. Every change Tiempo makes is recorded as an event and sent to your webhook endpoint.

[**Place a booking**Hold, confirm, or both at once.](#bookings-create)[**Find open times**Across one resource or many.](#availability-search)[**Receive webhooks**Signed, retried for three days.](#webhooks)

Base URL

`https://api.tiempo.one`

## Authentication

Send your secret key as a bearer token on every request. Keys belong to your platform and are created in the [dashboard](https://tiempo.one/app/keys). Keep them on your servers, never in browsers or mobile apps.

`tpt_…`

Test key. Works on test data only. Issued when you sign up.

`tpk_…`

Live key. Real businesses and real bookings. Issued when you activate live mode.

`tpw_…`

Webhook signing secret, one per endpoint. Verifies deliveries; can’t call the API.

Keys are shown once. Tiempo stores only a hash, so a lost key can’t be recovered: create another and revoke the old one. Each key carries a checksum, so a mistyped key fails fast and secret scanners can recognise leaked ones.

Authenticated request

```bash
curl https://api.tiempo.one/v1/accounts \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…"
```

## Acting for a business

Each business on your platform is an account. Requests that read or change a business’s resources, schedules or bookings carry its id in the `Tiempo-Account` header. Accounts, webhook endpoints, events and usage belong to your platform and don’t need it.

Accounts are isolated. An id from one account returns 404 when used under another, so a bug in your code can’t leak one business’s bookings into another’s.

Headers

```
Authorization: Bearer tpt_…
Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw
Content-Type: application/json
```

## Test and live mode

Test and live are separate worlds, and the key you send decides which one a request touches. Every object carries `livemode` so you can tell them apart in logs and webhooks.

TEST

Real behaviour against test data, including holds, races and webhooks. Counted, never billed.

● LIVE

Real businesses and real bookings. Confirmed bookings beyond the free allowance are billed.

## Requests and lists

Timestamps are ISO 8601 instants in UTC. Local times are `HH:mm`, local dates `YYYY-MM-DD`, timezones IANA names. Updates are `POST` with only the fields you’re changing.

Lists return `{ "object": "list", "data": [...], "has_more": true }`, newest first. Page with `limit` (up to 100) and `starting_after` or `ending_before` an id from the list. Filter on your own keys with `metadata[key]=value`.

Unknown body fields and unknown query parameters are rejected with `400 parameter_unknown`, so a typo fails loudly instead of quietly returning the wrong thing.

Send an `Idempotency-Key` header on any POST to retry it safely: the first response is stored for 24 hours and replayed (with `Idempotent-Replayed: true`). The same key with a different body is a 400.

Retrying safely

```bash
curl -X POST https://api.tiempo.one/v1/bookings \
  -H "Authorization: Bearer tpt_4qZ8m1VwXc0L…" \
  -H "Tiempo-Account: acct_01m42yt93ayvyrwr3yczhv6yhw" \
  -H "Idempotency-Key: apt_8812-create" \
  -H "Content-Type: application/json" \
  -d '{"segments": [...]}'
```

## Versioning

Breaking changes ship as dated versions (the latest is `2026-10-04`). Your platform is pinned to the version that was current when you signed up, so nothing changes under you until you upgrade in the dashboard. You can roll back for 72 hours.

Send `Tiempo-Version` to try another version on one request; every response says which version it used. Adding fields, endpoints, event types or enum values isn’t breaking, so ignore fields you don’t know and treat enums as open.

One request in another version

```
Tiempo-Version: 2026-10-04
```

## Errors

2xx means it worked, 4xx means the request can be fixed, and 5xx means Tiempo had a problem. Errors share one shape: `type`, a machine-readable `code`, a `message` for people, and `param` naming the field at fault.

-   `200`It worked.
-   `400`The request is malformed, a parameter is unknown, or a value is invalid. error.param names it.
-   `401`No key, or a revoked one.
-   `403`The key can’t do this, for example a test key on live data, or the platform is suspended (platform_suspended).
-   `404`No such object for this business, or it belongs to another.
-   `409`The time isn’t free, or the object is in the wrong state for this action.
-   `413`The request body is over 1 MB.
-   `429`Too many requests: over your platform’s limit for the second (rate_limited). Wait Retry-After seconds and try again.
-   `5xx`Something went wrong at Tiempo. Safe to retry with the same Idempotency-Key.

A `409 booking_conflict` means the time isn’t free, with a reason per segment. Offer the customer other times; retrying the same slot won’t help.

Every response carries a `Request-Id` header (`req_…`). Log it with errors: quoting it to us finds exactly that request.

Each platform can make 100 requests a second in live mode and 25 in test mode; ask us if you need more. Every response says where you are with `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds). Over the limit you get `429 rate_limited` with `Retry-After`: wait that long and retry with the same `Idempotency-Key`.

409 Conflict

```json
{
  "error": {
    "type": "conflict_error",
    "code": "booking_conflict",
    "message": "The requested time is not available.",
    "conflicts": [
      {
        "segment": 0,
        "resource": "res_01m42ytc9b53589z4rp11zedjy",
        "reason": "capacity_exceeded",
        "message": "Resource does not have enough free capacity during the period."
      }
    ]
  }
}
```

## Webhooks

Every change is recorded as an event in the same transaction as the change, then POSTed to each of your endpoints that wants it. One endpoint hears from every business on your platform.

-   ✓Answer with any 2xx within 10 seconds. Do slow work after you reply.
-   ◷Anything else is retried with backoff for about three days. If every delivery to an endpoint fails for three days, Tiempo turns it off; turn it back on and resend what it missed.
-   —Deliveries can arrive more than once or out of order. Use the event id to skip duplicates.

Check `Tiempo-Signature` with the endpoint’s `tpw_` secret before trusting a delivery. For local development, run `java tools/WebhookListener.java` from the Tiempo repo; it registers itself and checks every signature.

Verify a delivery (Node)

```js
import crypto from 'node:crypto'

// rawBody: the request body exactly as received (a string)
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
  const age = Math.abs(Date.now() / 1000 - Number(parts.t))
  if (age > 300) return false // older than 5 minutes: a replay
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}
```

## Reference by topic

-   [Accounts](https://tiempo.one/docs/accounts)An account is one business on your platform: a salon, a padel club. Create one as each business signs up to your product and keep its id next to your own record. Platform-level: no Tiempo-Account header.
-   [Locations](https://tiempo.one/docs/locations)A place resources work at, with its own timezone. Locations can be closed with blocks.
-   [Resources](https://tiempo.one/docs/resources)Anything bookable: a worker, a room, a court. A resource is bookable only inside its schedules: one with none is closed until you give it hours. Capacity above 1 is a pool of identical units.
-   [Schedules](https://tiempo.one/docs/schedules)Working hours for one resource, versioned by date. Several weeks make a rotation. Times are local wall-clock, so 09:00 stays 09:00 through daylight saving.
-   [Schedule overrides](https://tiempo.one/docs/schedule-overrides)Replace one resource’s hours for one local date. An empty list means not working that day.
-   [Blocks](https://tiempo.one/docs/blocks)Time off, closures, maintenance, lunch. A block targets a resource, a location, or the whole business, for a period or for whole local dates.
-   [Availability](https://tiempo.one/docs/availability)Ask what can be booked before you show times to a customer. The same engine checks every booking, so a time it offers will be accepted unless someone takes it first.
-   [Bookings](https://tiempo.one/docs/bookings)Time held on one or more resources. A booking starts pending (a hold that expires after 15 minutes by default) and becomes confirmed when you confirm it. Placing a booking locks its resources first, so when two requests race for the same time exactly one wins.
-   [Attendees](https://tiempo.one/docs/attendees)A booking with seats is a session (a class, a group). Each attendee takes seats with the same hold-then-confirm flow. Never oversold.
-   [Series](https://tiempo.one/docs/series)Recurring bookings from an RRULE, created as real bookings over a rolling horizon. A date that doesn’t fit is skipped and reported, not failed.
-   [Events](https://tiempo.one/docs/events)Every change is an event, written in the same transaction as the change. The same events go to your webhook endpoints; this API lets you catch up if your endpoint was down.
-   [Webhook endpoints](https://tiempo.one/docs/webhook-endpoints)Where Tiempo sends events. One endpoint hears from every business on your platform. Each endpoint reports its health: when it last worked, and since when it has been failing. One that fails every delivery for 3 days is turned off (disabled_reason "failing") until you turn it back on; then resend what it missed. Platform-level: no Tiempo-Account header.
-   [Usage](https://tiempo.one/docs/usage)Bookings counted towards your bill, per month and per business. Platform-level.

The machine-readable description is at [/openapi.json](https://tiempo.one/openapi.json). Try calls in [Swagger UI](https://tiempo.one/swagger-ui.html).[Tiempo home](https://tiempo.one/)[Dashboard](https://tiempo.one/app)
