# API keys

Secret and publishable clndr.pro keys, their scopes, where each one may live, and how to rotate or revoke them without downtime.

Every API request carries a key, and the key decides two things: whose booking pages and bookings the request can see, and what it can do with them. Each key belongs to the clndr.pro account that created it. A key can't reach another account's pages, so if your site shows booking pages for three people, each of them creates a key (or you use the [embed](/embed/script), which needs none).

## Two types

|                                                       | Secret                                   | Publishable                         |
| ----------------------------------------------------- | ---------------------------------------- | ----------------------------------- |
| Prefix                                                | `clndr_sk_`                              | `clndr_pk_`                         |
| Lives in                                              | Your server: env vars, a secrets manager | Browser bundles, mobile apps, HTML  |
| Env var we use in these docs                          | `CLNDR_SECRET_KEY`                       | `NEXT_PUBLIC_CLNDR_PUBLISHABLE_KEY` |
| Rate limit                                            | 300 requests per 60 s                    | 120 requests per 60 s               |
| Read booking pages and questions                      | Yes                                      | Yes                                 |
| Read open slots                                       | Yes                                      | Yes                                 |
| Create bookings                                       | Yes                                      | Yes                                 |
| List and read bookings (guest names, emails, answers) | Yes                                      | No                                  |
| Confirm and cancel bookings                           | Yes                                      | No                                  |

In scope terms: publishable keys hold `booking_pages:read`, `booking_pages:slots` and `bookings:create`. Secret keys hold those plus `bookings:read` and `bookings:write`. A request outside a key's scopes gets `403 API key is missing required scope(s): …`. The dashboard gives every key its type's full set; narrower custom scopes aren't available yet.

## Create a key

**Open API Keys**

Sign in at [www.clndr.pro](https://www.clndr.pro/dashboard) and open [**API keys**](https://www.clndr.pro/api-keys) in the sidebar.

**Choose the type and a name**

Choose **Create API Key**, pick **Secret key** or **Publishable key**, and name it after where it will be used: "Marketing site", "CRM sync job". The name is only shown to you.

**Copy it now**

The full key is shown once. clndr.pro stores a hash and the first 16 characters (for example `clndr_sk_4f1c2a9`), so a lost key can't be recovered, only replaced.

You can create five keys an hour.

## Where keys go

The rule is the prefix. A `clndr_sk_` key never reaches a browser; a `clndr_pk_` key can go anywhere.

In Next.js, only variables that start with `NEXT_PUBLIC_` are copied into client bundles, at build time. That makes the naming do the work:

```bash title=".env.local"
# Server only. Read it in server components, server actions, route handlers.
CLNDR_SECRET_KEY=clndr_sk_...

# Inlined into the browser bundle. Safe, and visible to anyone.
NEXT_PUBLIC_CLNDR_PUBLISHABLE_KEY=clndr_pk_...
```

Don't put a secret key in a `NEXT_PUBLIC_` variable, and don't read `process.env.CLNDR_SECRET_KEY` in a file marked `'use client'`. Next.js leaves non-public variables out of client code, so that read gives you `undefined` in the browser: the SDK throws ``Clndr: `apiKey` is required`` when you build the client, and a hand-written `fetch` gets `401`. Adding `import 'server-only'` to the file that builds your secret-key client turns the mistake into a build error instead.

Because public variables are baked in at build time, changing `NEXT_PUBLIC_CLNDR_PUBLISHABLE_KEY` needs a rebuild, not just a restart.

### On Vercel

```bash
vercel env add CLNDR_SECRET_KEY production
vercel env add NEXT_PUBLIC_CLNDR_PUBLISHABLE_KEY production
```

Run them again with `preview` and `development` if those environments call clndr.pro too. You can use the same keys everywhere, or create separate ones so revoking a leaked preview key doesn't touch production.

## What each key exposes

A **publishable key** is public the moment you deploy it: anyone can read it from your page source. With it they can list every booking page on your account (including inactive and private ones), read their questions and open slots, and book those open slots, the same as a guest on your site could. Bookings made with a publishable key must match a slot the API is offering right now, so the key can't drop meetings outside your hours or past the page's `max_days_ahead`. They can't see who booked, change a booking, or touch your pages or availability. If a private page shouldn't be discoverable at all, keep it off accounts whose publishable keys you ship, or book it through your server with the secret key instead.

A **secret key** reads every booking on the account: guest names, email addresses, times and answers. It can also confirm and cancel bookings. It can't edit booking pages, availability, access codes or account settings; those exist only in the dashboard.

## Rotate a key

Rotation doesn't need downtime, because two keys can be active at once:

1. Create a new key of the same type.
2. Put it in your env vars and deploy.
3. Check the old key's **Last used** date in the dashboard. Once it stops moving, choose **Revoke** on it.

**Revoke** switches a key off and keeps it in the list, so you can still see when it was last used. Requests with it get `401 API key has been revoked`. **Delete** removes it entirely; requests then get `401 Invalid API key`. Revoke first, delete later.

Keys don't expire. The API honours an expiry date on a key, but the dashboard has no way to set one yet.

## If a secret key leaks

Revoke it in the dashboard straight away; it stops working on the next request. Then create a replacement, deploy it, and look through [**Meetings**](https://www.clndr.pro/meetings) for bookings that were cancelled or confirmed without you. The key never had access to your account itself, so there's no password to change.

If it was committed to git, revoking is what protects you. Rewriting history doesn't remove copies that were already pulled or forked.

## Sending the key

Use either header. They're equivalent:

```http
Authorization: Bearer clndr_sk_...
x-clndr-key: clndr_sk_...
```

Always send requests to `https://www.clndr.pro/api/v1`. The bare `clndr.pro` host redirects to `www`, and HTTP clients strip `Authorization` when a redirect changes host, so the request arrives without a key and fails with `401 Missing API key`.
