Add entries and bookings with the API
Webhooks send what happens on your website to other apps. The API goes the other way: with a key you create in the Cloud tab, Zapier, Make, n8n or your own systems add entries to your tables, add bookings of times and places in classes, and read entries. Included in every plan and in the trial, without credits.
Create an API key
Open Cloud in the editor and scroll to Connections, then API keys.
Click Create an API key, give it the name of the service that will use it, like Zapier, and tick what it may do.
Click Create key and copy the key that starts with
fsk_. frascati shows it this once: it keeps only a fingerprint of it and cannot show it again.
- Read entries
- Read the entries of your tables, visitors' entries too, never orders. Its name in the API:
entries:read. - Add entries
- Add entries to your tables. Its name in the API:
entries:write. - Add bookings
- Add bookings of times and places in classes. Its name in the API:
bookings:write.
A key that adds or reads entries may also list your tables with their columns, and every key may read your services and your classes' dates, which its work needs. Make one key per service: Revoke stops a key at once and leaves the others working. A website has up to 10 keys. Whoever has a key can do what it allows, so keep it out of your website's code and out of shared documents.
Keys work while you have a plan or a trial. Without one, frascati makes no new key, and every request with a key answers plan_required; your keys stay and work again once you choose a plan.
Requests
Every address starts with https://frascati.app/api/v1. Send the key in the Authorization header as Bearer and the key, and bodies as JSON with Content-Type: application/json. Answers are JSON too. The API is for servers and tools like Zapier: it answers no browser on another website.
curl https://frascati.app/api/v1/key \
-H "Authorization: Bearer fsk_your_key"The answer names the website, the key and its permissions. Zapier and Make use such a request to test a connection.
- GET /api/v1/key
- The key's website and permissions.
- GET /api/v1/tables
- Your tables, with their columns and how many entries each has. Needs Read entries or Add entries.
- GET /api/v1/tables/{table}/entries
- The entries of a table, newest first. Needs Read entries.
- POST /api/v1/tables/{table}/entries
- Adds an entry. Needs Add entries.
- GET /api/v1/booking
- Your online booking: services, team, time zone.
- GET /api/v1/booking/times
- The free start times of a day.
- POST /api/v1/bookings
- Adds a booking of a time. Needs Add bookings.
- GET /api/v1/classes
- Your classes and events and their coming dates with the places left.
- POST /api/v1/class-bookings
- Adds places on a date of a class or an event. Needs Add bookings.
Limits and errors
A key makes up to 60 requests a minute. Every answer says how many are left in X-RateLimit-Remaining; past the limit the answer is 429 with Retry-After, the seconds until the next minute. A website adds up to 200 bookings, of times and places in classes together, and 1,000 entries a day through the API, counted per day in UTC; past that the answer is 429 daily_limit with Retry-After, the seconds until midnight UTC. An error is JSON with a code to act on and a sentence to read: { "error": { "code": "missing_values", "message": "Fill in: name.", "fields": ["name"] } }.
400 invalid_json, invalid_request, invalid_cursor
- What it means
- The body is no JSON object, a part of the address is missing, or the cursor is from another table.
401 missing_key, invalid_key, revoked_key
- What it means
- No key, a wrong key, or one you revoked.
403 missing_permission
- What it means
- The key may not do this. Create one with the permission.
403 plan_required
- What it means
- The website's owner has no plan and no trial. The keys work again once there is one.
403 limit_reached
- What it means
- Your plan's entries are used up.
404 not_found
- What it means
- No such table (Orders is not in the API), or no such address in the API.
409 needs_force
- What it means
- The time or the date needs a look. The answer says why; send it again with
"force": trueto book it anyway.
409 booking_not_set_up, classes_not_set_up, date_cancelled
- What it means
- The website takes no bookings of this kind, or you cancelled the date.
422 missing_values, invalid_values, empty_entry
- What it means
- Something to fix in the entry;
fieldsnames the columns.
422 invalid_booking, unknown_service, unknown_class, not_a_date
- What it means
- Something to fix in the booking: the service, the class, the date or the name.
429 rate_limited
- What it means
- More than the requests of a minute, or more wrong or revoked keys from one address than a minute takes.
429 daily_limit
- What it means
- The website added what a day allows through the API.
Retry-Aftersays the seconds until midnight UTC.
500 server_error
- What it means
- Something went wrong on our side. Try again in a moment.
| Status and code | What it means |
|---|---|
400 invalid_json, invalid_request, invalid_cursor | The body is no JSON object, a part of the address is missing, or the cursor is from another table. |
401 missing_key, invalid_key, revoked_key | No key, a wrong key, or one you revoked. |
403 missing_permission | The key may not do this. Create one with the permission. |
403 plan_required | The website's owner has no plan and no trial. The keys work again once there is one. |
403 limit_reached | Your plan's entries are used up. |
404 not_found | No such table (Orders is not in the API), or no such address in the API. |
409 needs_force | The time or the date needs a look. The answer says why; send it again with "force": true to book it anyway. |
409 booking_not_set_up, classes_not_set_up, date_cancelled | The website takes no bookings of this kind, or you cancelled the date. |
422 missing_values, invalid_values, empty_entry | Something to fix in the entry; fields names the columns. |
422 invalid_booking, unknown_service, unknown_class, not_a_date | Something to fix in the booking: the service, the class, the date or the name. |
429 rate_limited | More than the requests of a minute, or more wrong or revoked keys from one address than a minute takes. |
429 daily_limit | The website added what a day allows through the API. Retry-After says the seconds until midnight UTC. |
500 server_error | Something went wrong on our side. Try again in a moment. |
Add and read entries
curl https://frascati.app/api/v1/tables \
-H "Authorization: Bearer fsk_your_key"curl -X POST https://frascati.app/api/v1/tables/requests/entries \
-H "Authorization: Bearer fsk_your_key" \
-H "Content-Type: application/json" \
-d '{"name": "Lena Graf", "email": "lena@example.com", "message": "A quote for a new kitchen"}'Send the values by the columns' keys, as GET /tables lists them, or by the columns' names: Zapier and Make send what you type. They are checked like in the Cloud tab: a price may be 18.50, 18,50 or CHF 18.50, an email must be one, a choice one of its options. A required column left out or a value that does not fit answers 422 with the columns in fields; names that are no column come back in ignored. The answer 201 has the entry as it was saved.
- In a table your visitors fill, like requests or a waitlist, an entry from the API is one like theirs: it shows under the table, and your webhooks get
entry.createdwithsourceset toapi. You get no email for it. - In a table your website shows, like a menu or a price list, it goes to the end of the list. Send
{ "data": { … }, "hidden": true }to keep it off the website for now. - It counts toward your plan's entries per website: 300 with Starter, 5,000 with Pro, 50,000 with Business.
- The table Orders is not in the API, neither to read nor to add to: it holds your buyers' names, addresses and payments. Each paid order reaches Zapier, Make or n8n through the webhook
order.paid.
curl "https://frascati.app/api/v1/tables/requests/entries?limit=20" \
-H "Authorization: Bearer fsk_your_key"limit is 50 unless you say, at most 100. When there are more, the answer's next holds a cursor: send it as cursor for the next page. Each entry has its id, createdAt and its values in data, in a table your website shows also whether it is hidden.
Add bookings
curl https://frascati.app/api/v1/booking \
-H "Authorization: Bearer fsk_your_key"curl "https://frascati.app/api/v1/booking/times?service=cut&date=2026-10-20" \
-H "Authorization: Bearer fsk_your_key"curl -X POST https://frascati.app/api/v1/bookings \
-H "Authorization: Bearer fsk_your_key" \
-H "Content-Type: application/json" \
-d '{"service": "cut", "date": "2026-10-20", "time": "10:00", "name": "Lena Graf", "email": "lena@example.com", "phone": "+41 79 000 00 02", "note": "Booked by phone"}'A booking from the API is one you add yourself under Add booking in the Cloud tab: confirmed at once, with the person of your team you name in staff, else whoever is free. A time that overlaps other bookings with no place left, lies outside your opening hours, on a closed day or in the past answers 409 with needs_force and what is wrong; send the same again with "force": true to book it anyway. service, date, time and name are required; people is for tables, email, phone and note are optional.
frascati sends the guest no email for a booking from the API, neither a confirmation nor reminders: the system that made it tells the guest. It shows in the Cloud tab as added through the API, in your calendar link and your CSV file, and your webhooks get booking.created with source set to api.
Places in classes and events
curl https://frascati.app/api/v1/classes \
-H "Authorization: Bearer fsk_your_key"curl -X POST https://frascati.app/api/v1/class-bookings \
-H "Authorization: Bearer fsk_your_key" \
-H "Content-Type: application/json" \
-d '{"session": "vinyasa~2026-10-19~18:30", "seats": 2, "name": "Lena Graf", "email": "lena@example.com"}'session comes from GET /classes; class, date and time work too. The date must be one of the class's dates and not cancelled. When it has fewer places left than you ask for, or is over, the answer is 409 with needs_force and left or the warning past; with "force": true the guest gets the places anyway, as when you add a guest in the Cloud tab. No email goes to the guest here either.
Zapier
Add an action Webhooks by Zapier with the event POST.
URL: the address, like
https://frascati.app/api/v1/tables/requests/entries. Payload Type: json.Data: one line per column, the column's key or name on the left, the value from your trigger on the right.
Headers:
AuthorizationwithBearer fsk_your_key. Then Test step: Zapier shows frascati's answer.
Make
Add the module HTTP and Make a request.
URL: the address. Method: POST. Headers:
AuthorizationwithBearer fsk_your_key.Body type: Raw, content type JSON (application/json), and as request content the JSON with your values, like
{"name": "{{1.name}}", "email": "{{1.email}}"}. Tick Parse response.
n8n
Add an HTTP Request node. Method: POST, URL: the address.
Authentication: Generic Credential Type, then Header Auth, with the name
Authorizationand the valueBearer fsk_your_key.Turn on Send Body, choose JSON and add your values, by the columns' keys.
What the API does not do yet
- No app of its own in Zapier's or Make's list and no sign-in with OAuth: you connect with their HTTP steps and a key.
- Entries and bookings are added, not changed or deleted; that stays in the Cloud tab.
- Bookings are not read through the API. Your webhooks send each new, confirmed and cancelled one, and the calendar link under Bookings has them all.
- No emails to guests for bookings from the API.
- Keys belong to one website; a key for all websites of an account does not exist.
- No orders, payments, products, form messages or website content through the API. Paid orders come through the webhook
order.paid. - No two-way calendar sync: your bookings go to Google Calendar, Apple Calendar or Outlook through the calendar link, read only, and a busy time there does not block a time on your website.
- No connection to Google Business Profile, a CRM or a newsletter tool of its own; they go through Zapier, Make or n8n.
Something missing or unclear? Write to us.