Developers

Below you'll find information, guides and tutorials helping you get flying with a custom Timekit integration in no time.

Group & class bookings

Introduction

Through the /booking endpoint you are able to set up groups and classes, so that multiple customers can sign up for the same event. You can use the Timekit API to manage everything from your meetings and sales calls to your webinars and yoga classes. Speaking of! Let's have a look at how you would use Timekit to manage the yoga studio you've always wanted to open:

What is a group booking?

Timekit offer many different booking flows, each represented as a graph. When you are working with group bookings, you are interacting with two different graphs: The group_owner graph and either the group_customer or the group_customer_payment graph.

The group_owner graph is used to reserve a time slot in the owners's calendar and make it available for customers to book as a group. You can think of this as the owners availability. If you are going to offer a yoga class on Friday at 7pm, you simply need to add a booking using the group_owner graph.

When a group booking is set up, customers can book a seat. This is done using the group_customer graph or, if your system involves payments, the group_customer_payment graph. Customers can sign up for a group booking until there are no available seats left.

1. Define availability

Before customers can sign up to our yoga class, we need to configure our availability as owner. This is done by adding a owner booking using the group_owner graph.

Set up availability

For this example Doc Brown (Yes! That Doc Brown) is offering a yoga class with 20 seats, at his yoga studio on Sesame St. Middleburg, FL 32068, USA. It takes place on September 30, 2016 at 9:00 - and you don't want to miss it!

Using the group_owner graph, we can use the following request to set up the class:

# Request example (replace :calendar-id)
# [POST] /bookings
curl --request POST \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "graph": "group_owner",
          "settings": {
            "max_seats": 20
          },
          "event": {
            "start": "2016-09-30T08:00:00+00:00",
            "end": "2016-09-30T09:00:00+00:00",
            "what": "Doc Brown's Friday Yoga Class",
            "where": "Sesame St, Middleburg, FL 32068, USA",
            "calendar_id": "replace-with-id",
            "description": "Please arrive 10 minutes before classes begin"
          }
        }' \
     https://api.timekit.io/v2/bookings

Notice that we are using RFC3339 standard for the timestamp.

Now, the yoga class will be available for customers to sign up for. Hurry up before it sells out!

Optionally, you can specify some basic rules for when customers can sign up to your groups. Doc wants you to cancel at least a day before the class begins, book it no more than 14 days in advance and no later than 2 hours before. We can set it up like this:

# Request example (replace :calendar-id)
# [POST] /bookings
curl --request POST \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "graph": "group_owner",
          "settings": {
            "max_seats": 20,
            "min_cancel_time": "1 day",
            "min_booking_time": "2 hours",
            "max_booking_time": "14 days"
          },
          "event": {
            "start": "2016-09-30T08:00:00+00:00",
            "end": "2016-09-30T09:00:00+00:00",
            "what": "Doc Brown's Friday Yoga Class",
            "where": "Sesame St, Middleburg, FL 32068, USA",
            "calendar_id": "replace-with-id",
            "description": "Please arrive 10 minutes before classes begin"
          }
        }' \
     https://api.timekit.io/v2/bookings

Setting up multiple groups at once?

If you are setting up multiple groups at once, say a yoga class every Friday for the next 10 weeks, you will need to call the /bookings endpoint 10 times. We are working on a version of our /findtime endpoint to allow you to query availability in bulk.

Query availability

When customers visit Doc's website, we want them to see all his classes. Technically speaking, we want to list all his group bookings that are still in a tentative state - meaning that they are neither completed, nor cancelled. We can use the /bookings/groups endpoint to query Doc's availability. That looks like this:

# Request example
# [GET] /bookings/search
curl --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     https://api.timekit.io/v2/bookings/groups

And will return something like this:

{
  "data": [
    {
      "id": "0dad9c8f-2801-4b6f-b898-7c69063bed0d",
      "state": "tentative",
      "graph": "group_owner",
      "completed": false,
      "created_at": "2016-02-11T12:39:45+0100",
      "updated_at": "2016-02-11T13:12:01+0100",
      "attributes": {
        "event_info": {
          "start": "2016-09-30T08:00:00+00:00",
          "end": "2016-09-30T09:00:00+00:00",
          "what": "Doc Brown's Friday Yoga Class",
          "where": "Sesame St, Middleburg, FL 32068, USA",
          "calendar_id": "replace-with-id",
          "description": "Please arrive 10 minutes before classes begin"
        },
        "group_booking": {
          "current_seats": 0,
          "max_seats": 20,
        }
      }
    },
    {
      "id": "a1ebdd9d-db42-4cd3-a915-0202a034284a",
      "state": "tentative",
      "graph": "group_owner",
      "completed": false,
      "created_at": "2016-02-11T12:39:45+0100",
      "updated_at": "2016-02-11T13:12:01+0100",
      "attributes": {
        "event_info": {
          "start": "2016-10-07T08:00:00+00:00",
          "end": "2016-10-07T09:00:00+00:00",
          "what": "Doc Brown's Friday Yoga Class",
          "where": "Sesame St, Middleburg, FL 32068, USA",
          "calendar_id": "replace-with-id",
          "description": "Please arrive 10 minutes before classes begin"
        },
        "group_booking": {
          "current_seats": 0,
          "max_seats": 20,
        }
      }
    }
  ]
}

Looks like no one signed up so far!? Guess there's still time to sign up. Let's do it!

You can optionally specify a start and end query parameter, to only get bookings within a given period of time.

Is it safe to expose booking data?

If you publicly expose information about a group booking (similar to what we do in our Booking.js widget), you should always use the /bookings/groups endpoint, as opposed to the regular /booking endpoint. This endpoint excludes all sensitive information about the group, such as customer information.

2a. Sign up for a group booking

It appears that a certain Marty McFly is interesting in joining the yoga class. I'm sure Doc will be pleased to know, so let's sign him up using the group_customer graph:

# Request example
# [POST] /bookings
curl --request POST \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "graph": "group_customer",
          "related": {
            "owner_booking_id": "a1ebdd9d-db42-4cd3-a915-0202a034284a"
          },
          "customer": {
            "name": "Marty McFly",
            "email": "Marty.McFly@example.com"
          }
        }' \
     https://api.timekit.io/v2/bookings

Only 19 spots left now. Hurry up!

It's possible for customers to book more than one seat at once, so actually, let's make that 18, because Marty is bringing a friend. This can be indicated with the seats option in our request:

# Request example
# [POST] /bookings
curl --request POST \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "graph": "group_customer",
          "action": "create",
          "related": {
            "owner_booking_id": "a1ebdd9d-db42-4cd3-a915-0202a034284a",
            "seats": 2
          },
          "customer": {
            "name": "Marty McFly",
            "email": "Marty.McFly@example.com"
          }
        }' \
     https://api.timekit.io/v2/bookings

Marty will now have reserved 2 seats out of the 20 available ones.

2b. Integrating payments for signups

If your booking flow involves some kind of payment option, you are able to indicate whether a customer has paid using the group_customer_payment graph. There are a few things that makes it differ from the standard group_customer graph. The first booking state, after creation, is “tentative” and this is when you will want to show your checkout window (e.g. using Stripe checkout.js). A seat is locked to the customer while in the tentative state.

Here's how the group_customer_payment graph looks:

The `group_customer_payment` graph.

The group_customer_payment graph.

If you successfully receive payment for the booking, you need to afterwards call the “pay” action on the booking to transition it into its “paid” state. This action takes a payment_id attribute so you can store a transaction ID together with the booking for future reference.

If the customer fails to pay within a timeout limit (default is 5 minutes), the booking will automatically transition into an “unpaid” state and the seat is released for another customer to book.

This time, the class was free. Next time, though, Marty will have to pay. Let's sign him up using the group_customer_payment graph to see how it looks:

# Request example
# [POST] /bookings
curl --request POST \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "graph": "group_customer_payment",
          "action": "create",
          "related": {
            "owner_booking_id": "a1ebdd9d-db42-4cd3-a915-0202a034284a"
          },
          "customer": {
            "name": "Marty McFly",
            "email": "Marty.McFly@example.com"
          }
        }' \
     https://api.timekit.io/v2/bookings

To indicate that Marty has paid, all we need to do is to update the payment state of his booking:

# Request example (replace :payment_id_string)
# [PUT] /bookings/9d3fc349-5605-4bbf-a8fd-270fee55c137/pay
curl --request PUT \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "pay": {
            "payment_id": ":payment_id_string"
          }
        }' \
     https://api.timekit.io/v2/bookings/9d3fc349-5605-4bbf-a8fd-270fee55c137/pay

You decide what to send as payment_id, but normally this would be something like a transaction ID from Stripe.

Use Stripe?

If you use Stripe, our group_customer_payment graph makes it easy to implement payments with Timekit.

When you have signed up a customer for a group booking, the booking will be in a tentative state. This is where you will show your checkout window (e.g. using checkout.js) and receive the payment. Once the payment is received and processed, you need to call the /pay endpoint on the booking in order to indicate the new state. When this is done, the booking will be in a paid state.

» Check out Stripe's Detailed Checkout Guide

3. Cancellations

Imagine a situation where Marty messed up his schedule (could you see this happen!?) and double booked himself. He would have to cancel yoga... Fortunately, that's pretty simple using the cancel_by_customer action and he can even send a message to Doc:

# Request example
# [PUT] /bookings/9d3fc349-5605-4bbf-a8fd-270fee55c137/cancel_by_customer
curl --request PUT \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "cancel_by_customer": {
            "message": "Double booked. Sorry, Doc!"
          }
        }' \
     https://api.timekit.io/v2/bookings/9d3fc349-5605-4bbf-a8fd-270fee55c137/cancel_by_customer

Even worse, Doc might have to cancel too! Then no one would get to yoga that Friday. That's done directly on the owner booking with the cancel action:

# Request example
# [PUT] /bookings/a1ebdd9d-db42-4cd3-a915-0202a034284a/cancel
curl --request PUT \
     --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     --data '{
          "cancel": {
            "message": "Getting the DeLorean fixed that day. Doc."
          }
        }' \
     https://api.timekit.io/v2/bookings/a1ebdd9d-db42-4cd3-a915-0202a034284a/cancel

Marty, and everyone else who signed up, will receive a notification with Doc's message attached. All is well!

4. Manage a group booking

Before class, Doc needs to know how many yoga mats to bring. When we request the owner booking, we can ask the API to include all related customer bookings. It looks like this:

# Request example
# [GET] /bookings/a1ebdd9d-db42-4cd3-a915-0202a034284a
curl --user :live_api_key_7nzvc7wsBQQISLeFSVhROys9V1bUJ1z7 \
     https://api.timekit.io/v2/bookings/a1ebdd9d-db42-4cd3-a915-0202a034284a?include=attributes,related_bookings

And returns something like:

{
  "data": {
    "id": "0dad9c8f-2801-4b6f-b898-7c69063bed0d",
    "state": "tentative",
    "graph": "group_owner",
    "completed": false,
    "created_at": "2016-02-11T12:39:45+0100",
    "updated_at": "2016-02-11T13:12:01+0100",
    "attributes": {
      "event_info": {
        "start": "2016-09-30T08:00:00+00:00",
        "end": "2016-09-30T09:00:00+00:00",
        "what": "Doc Brown's Friday Yoga Class",
        "where": "Sesame St, Middleburg, FL 32068, USA",
        "calendar_id": "replace-with-id",
        "description": "Please arrive 10 minutes before classes begin"
      },
      "group_booking": {
        "current_seats": 2,
        "max_seats": 20
      }
    },
    "related_bookings": [
  	  {
  			"id": "9d3fc349-5605-4bbf-a8fd-270fee55c137",
        "state": "confirmed",
        "graph": "group_customer",
        "completed": false,
        "created_at": "2016-02-11T12:39:45+0100",
        "updated_at": "2016-02-11T13:12:01+0100"
  		},
  		{
  			"id": "63550652-6b21-446c-b125-9a4d02bf6747",
        "state": "confirmed",
        "graph": "group_customer",
        "completed": false,
        "created_at": "2016-02-11T12:39:45+0100",
        "updated_at": "2016-02-11T13:12:01+0100"
  		}
    ]
  }
}

Because each customer who signs up to a group booking has an actual booking (following the group_customer_graph), related_bookings is a reference to all the customer bookings attached to the group.

Great scott, you've finished our guide! We hope that group bookings make sense to you and that you are ready to dive in. If not, hit us up on the chat and we'll help you get started.