Keycafe API

Authentication ↓↑

The API uses Basic HTTP Authentication with your account email and password.

curl -X POST -u '{email}:{password}' https://www.keycafe.com/v0/access

To create the Authorization header, encode "email:password" to base 64 and prepend it with "Basic " (i.e. "Authorization: Basic ZW1haWw6cGFzc3dvcmRPckFwaUtleQ=="). The -u curl option does this automatically.

API keys can also be used in lieu of passwords.

curl -X POST -u '{email}/key:{key}' https://www.keycafe.com/v0/access

An API key can be generated in your account settings.

Responses

Successful responses will have status code 200.

Errors will usually have status code 400 (validation error) or 500 (internal server error) and the response will contain a JSON object representing the specific errors:

{
    "error": {
        "errorId": "unauthorized",
        "errorMessage": "Unauthorized"
    },
    "fieldErrors": [
        {
            "field": "name",
            "errorMessage": "Please specify a key name."
        }
    ]
}
    
error.errorId
An error ID
error.errorMessage
A description of the error.
fieldErrors
An array of specific field errors.

Request Headers

Accept-Language
Specifies the locale to prioritize for all responses. If omitted or an unsupported locale is specified, the authenticated user's default locale will be used. The expected format includes case-insensitive ISO 639 alpha-2 language codes and ISO 3166 alpha-2 country codes. (i.e. en-US, fr_ca, etc...)

Rate Limiting

Requests are rate limited to 60 per 1 minute. Exceeding this limit will result in all requests failing with status 429 for 1 minute.

Authorization ↓↑

For accounts with two-factor authentication enabled or client implementations desiring additional security, authorization tokens can be used in place of passwords and API keys. Example:

curl -X POST -u '{email}/token:{token}' https://www.keycafe.com/v0/access

Manage authorization tokens via the RESTful endpoint: https://www.keycafe.com/v0/authorization

Create Authorization

Create an authorization with a POST request to the endpoint. If the account has two-factor authentication enabled or "twoFactor" is specified as true, the request will fail with status code 400, but will trigger the sending of a 2FA code to the user's mobile number. The request can then be attempted again with the code. If 2FA is disabled and "twoFactor" is omitted or false, the first request will succeed. Multiple authorization tokens can be retrieved for a single account.

curl -X POST -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/authorization -H "Content-Type: application/json" -H "Accept: application/json" -d '{...}'

The post body should contain the necessary data to create the authorization as a JSON object:

{
    "expiry": "2016-07-23T12:45:21Z",
    "code": "ABC123",
    "twoFactor": true
}
expiry
An optional (but recommended) expiry date for the authorization token. If omitted, the token never expires.
code
Required if the account has 2FA enabled or twoFactor is true. The code that was sent to the user.
twoFactor
If true, a 2FA code will be required to generate the authorization token, regardless of whether the user has 2FA enabled.

The response body will contain additional or updated information:

{
    "id": 123456,
    "expiry": "2016-07-23T12:45:21Z",
    "token": "4d14e0f406cb4066b52251d5b9bb7bee",
    "user": {
        "id": 123456,
        "email": "user@yourdomain.com",
        "mobile": "16042656073",
        "firstName": "Bob",
        "lastName": "Smith"
    },
}
id
A unique ID for the authorization. Used to invalidate the token.
expiry
The expiry date of the token. If omitted, the token never expires.
token
An authorization token that can be used in requests in place of a password or API token.
user
The authorized user.

Invalidate Authorization

Invalidate an authorization with a DELETE request to the endpoint with the authorization ID. Note that this uses the "id" property of the authentication, not the token.

curl -X DELETE -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/authorization/123456 -H "Accept: application/json"

Key ↓↑

Key management is made available via the RESTful endpoint: https://www.keycafe.com/v0/key

Retrieve Key

Retrieve data for a single key with a GET request to the endpoint with an ID.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/key/123456 -H "Accept: application/json"

The response body will contain the key's data as a JSON object. The current location property will only be present if the key is currently checked in. The current user property will only be present if the key is currently checked out.

{
    "id": 123456,
    "name": "Bob's Home Key",
    "serialNumber": "XXXXX",
    "notes": "The cleaners come every Monday at 10:00",
    "pickupNote": "Don't mind the dog. He's harmless.",
    "address": {
        "street": "123 Main St.",
        "unit": "1A",
        "city": "Vancouver",
        "stateProv": "BC",
        "country": "Canada",
        "postalCode": "A1A1A1"
    },
    "homeLocation": {
        "id": 123456,
        "name": "Corner Cafe",
        "timezone": "America/Vancouver",
        "address": {
            "street": "123 Main St.",
            "unit": "1A",
            "city": "Vancouver",
            "stateProv": "BC",
            "country": "Canada",
            "postalCode": "A1A1A1",
            "latitude": 9.2839043,
            "longitude": -123.1053527
        },
        "hours": [
            { "dayOfWeek": 0, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 1, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 2, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 3, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 4, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 5, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 6, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 }
        ]
    },
    "currentLocation": {
        "id": 123456,
        "name": "Corner Cafe",
        "timezone": "America/Vancouver",
        "address": {
            "street": "123 Main St.",
            "unit": "1A",
            "city": "Vancouver",
            "stateProv": "BC",
            "country": "Canada",
            "postalCode": "A1A1A1",
            "latitude": 9.2839043,
            "longitude": -123.1053527
        },
        "hours": [
            { "dayOfWeek": 0, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 1, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 2, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 3, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 4, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 5, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
            { "dayOfWeek": 6, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 }
        ]
    },
    "currentUser": {
        "id": 123456,
        "email": "user@yourdomain.com",
        "mobile": "16042656073",
        "firstName": "Bob",
        "lastName": "Smith"
    },
    "owner": {
        "id": 123456,
        "email": "user@yourdomain.com",
        "mobile": "16042656073",
        "firstName": "Bob",
        "lastName": "Smith"
    },
    "accesses": [
        {
            "id": 123456,
            "startDate": "2016-07-21T12:45:21Z",
            "endDate": "2016-07-23T12:45:21Z",
            "returnReminder": false
        },
        ...
    ]
}

The read-only "accesses" list indicates the nature of the logged in user's access to the key. If the list is omitted from the response, the key is owned by the user.

Search Keys

Retrieve data for multiple keys with a GET request to the endpoint.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/key?query=vancouver -H "Accept: application/json"

The search query can match the name, serial number, notes or address of the key. Omitting the search query will return all keys. The response body will contain the key data as a JSON array:

[
    { "id": 123456, "name": "Bob's Home Key", ...  },
    { "id": 654321, "name": "Bob's Office Key", ...  }
]

Results are limited to 50 per page, with additional pages available via the Link response header:

Link: <https://www.keycafe.com/v0/key?query=vancouver&page=2>; rel="next", <https://www.keycafe.com/v0/key?query=vancouver&page=5>; rel="last"

Create Key

Create a key with a POST request to the endpoint.

curl -X POST -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/key -H "Content-Type: application/json" -H "Accept: application/json" -d '{...}'

The post body should contain the necessary data to create the key as a JSON object:

{
    "name": "Bob's Home Key",
    "serialNumber": "XXXXX",
    "notes": "The cleaners come every Monday at 10:00",
    "pickupNote": "Don't mind the dog. He's harmless.",
    "address": {
        "street": "123 Main St.",
        "unit": "1A",
        "city": "Vancouver",
        "stateProv": "BC",
        "country": "Canada",
        "postalCode": "A1A1A1"
    },
    "currentUser": { "id": 123456, "email": "users@yourdomain.com", "mobile": "16042656073" }, 
    "homeLocation": { "id": 123456 }
}
name
Required. The key name.
serialNumber
The serial number of the fob attached to the key. If omitted, it will be collected during the key's first drop off.
notes
Internal notes. These will not be displayed to the guest.
pickupNote
A note to send to the guest after a pickup.
address
A nested object containing the location address.
address.street
The street address.
address.unit
The address unit number.
address.city
The address city.
address.stateProv
The address state or province.
address.country
The address country.
address.postalCode
The address postal code.
currentUser
A user capable of activating this key by dropping it off for the first time. If the key has already been dropped off, this property is ignored. The user can be specified by ID, email or mobile number, and they will receive a notification to drop off the key.
homeLocation
The location where the key is expected to be dropped off. This is used to communicate to a user where they should drop the key off, and should not be confused with the currentLocation property, which is read-only and can only be modified by a drop off. A drop off will also update the key's home location to the most recent drop off location, and this property may only be set while the key is checked out. Only the ID property is necessary to set a home location.

The response body will contain the same information with the key ID property added.

Edit Key

Edit a key with a PUT request to the endpoint with the key ID.

curl -X PUT -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/key/123456 -H "Content-Type: application/json" -H "Accept: application/json" -d '{...}'

The post body should contain the data to be updated as a JSON object:

{
    "name": "Bob's Home Key",
    "notes": "The cleaners come every Monday at 10:00",
    "pickupNote": "Don't mind the dog. He's harmless.",
    "address": {
        "street": "123 Main St.",
        "unit": "1A",
        "city": "Vancouver",
        "stateProv": "BC",
        "country": "Canada",
        "postalCode": "A1A1A1"
    },
    "currentUser": { "id": 123456, "email": "users@yourdomain.com", "mobile": "16042656073" },
    "homeLocation": { "id": 123456 }
}
name
The key name.
notes
Internal notes. These will not be displayed to the guest.
pickupNote
A note to send to the guest after a pickup.
address
A nested object containing the location address.
address.street
The street address.
address.unit
The address unit number.
address.city
The address city.
address.stateProv
The address state or province.
address.country
The address country.
address.postalCode
The address postal code.
currentUser
A user capable of activating this key by dropping it off for the first time. If the key has already been dropped off, this property is ignored. The user can be specified by ID, email or mobile number, and they will receive a notification to drop off the key.
homeLocation
The location where the key is expected to be dropped off. This is used to communicate to a user where they should drop the key off, and should not be confused with the currentLocation property, which is read-only and can only be modified by a drop off. A drop off will also update the key's home location to the most recent drop off location, and this property may only be set while the key is checked out. Only the ID property is necessary to set a home location.

Retire Key

Retire a key with a DELETE request to the endpoint with the key ID.

curl -X DELETE -u 'test@yourdomain.com:{password}' -H "Accept: application/json" 'https://www.keycafe.com/v0/key/123456'

Access ↓↑

Access management is made available via the RESTful endpoint: https://www.keycafe.com/v0/access

Retrieve Access

Retrieve data for a single access with a GET request to the endpoint with an ID.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/access/123456 -H "Accept: application/json"

The response body will contain the access data as a JSON object:

{
    "id": 123456,
    "user": {
        "id": 123456,
        "email": "user@yourdomain.com",
        "mobile": "16042656073",
        "firstName": "Bob",
        "lastName": "Smith"
    },
    "key": {
        "id": 123456,
        "name": "Bob's Home Key",
        "serialNumber": "XXXXX",
        "notes": "The cleaners come every Monday at 10:00",
        "pickupNote": "Don't mind the dog. He's harmless.",
        "address": {
            "street": "123 Main St.",
            "unit": "1A",
            "city": "Vancouver",
            "stateProv": "BC",
            "country": "Canada",
            "postalCode": "A1A1A1"
        }
    },
    "startDate": "2016-07-21T12:45:21Z",
    "endDate": "2016-07-23T12:45:21Z",
    "openedAt": "2016-07-21T17:38:38Z",
    "confirmed": false,
    "returnReminder": false
}

For privacy reasons, only the contact information (email/mobile) provided when the access was created will be returned in the user object.

Search Accesses

Retrieve data for multiple accesses with a GET request to the endpoint.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/access?query=bob -H "Accept: application/json"

The search query can match the name, email, or mobile number of the guest, or the name, serial number, notes or address of the key. Omitting the search query will return all accesses. The response body will contain the access data as a JSON array:

[
    { "id": 123456, ... },
    { "id": 654321, ... }
]

A search can also be peformed using a key ID:

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/access?key=123456 -H "Accept: application/json"

The results will include all ongoing or future accesses for that key.

A search can also be peformed using a user ID:

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/access?user=123456 -H "Accept: application/json"

The results will include all ongoing or future accesses for that user.

Results are limited to 50 per page, with additional pages available via the Link response header:

Link: <https://www.keycafe.com/v0/access?query=bob&page=2>; rel="next", <https://www.keycafe.com/v0/access?query=bob&page=5>; rel="last"

Create Access

Create an access with a POST request to the endpoint.

curl -X POST -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/access -H "Content-Type: application/json" -H "Accept: application/json" -d '{...}'

The post body should contain the necessary data to create the access as a JSON object:

{
    "user": { "id" : 123456, "email": "user@yourdomain.com", "mobile": "16042656073", "firstName": "Bob", "lastName": "Smith" },
    "key": { "id" : 123456, "serialNumber": "XXXXX" },
    "startDate": "2016-07-21T12:45:21Z",
    "endDate": "2016-07-23T12:45:21Z",
    "returnReminder": false
}
user
Required. The user being give access. Only the ID property is necessary if it's an existing user. Otherwise, an email address or mobile number can be included to invite a new user.
key
Required. The key for which access is being given. Only one of the ID or serialNumber property is necessary.
startDate
The start date of the access. All dates are specified using ISO 8601 format. If both startDate and endDate are omitted, the access is considered ongoing.
endDate
The end date of the access. All dates are specified using ISO 8601 format. If both startDate and endDate are omitted, the access is considered ongoing.
returnReminder
Default false. Whether the guest should be sent a reminder to return the key after their visit.

The response body will contain the same information with the access ID property added.

Cancel Access

Cancel an access with a DELETE request to the endpoint with the access ID.

curl -X DELETE -u 'test@yourdomain.com:{password}' -H "Accept: application/json" 'https://www.keycafe.com/v0/access/123456'

Resend Access

Resend access notifications with a PUT request to the endpoint with the resend operation.

curl -X PUT -u 'test@yourdomain.com:{password}' -H "Accept: application/json" 'https://www.keycafe.com/v0/access/123456/resend'

Access notifications can be sent a maximum of once per hour.

Location ↓↑

Location information is made available via the RESTful endpoint: https://www.keycafe.com/v0/location

Retrieve Location

Retrieve data for a single location with a GET request to the endpoint with an ID.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/location/123456 -H "Accept: application/json"

The response body will contain the location's data as a JSON object:

{
    "id": 123456,
    "name": "Corner Cafe",
    "timezone": "America/Vancouver",
    "capacity": 10,
    "address": {
        "street": "123 Main St.",
        "unit": "1A",
        "city": "Vancouver",
        "stateProv": "BC",
        "country": "Canada",
        "postalCode": "A1A1A1",
        "latitude": 9.2839043,
        "longitude": -123.1053527
    },
    "hours": [
        { "dayOfWeek": 0, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
        { "dayOfWeek": 1, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
        { "dayOfWeek": 2, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
        { "dayOfWeek": 3, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
        { "dayOfWeek": 4, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
        { "dayOfWeek": 5, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 },
        { "dayOfWeek": 6, "startMinuteOfDay": 360, "endMinuteOfDay": 1320 }
    ],
    "photo": "https://www.keycafe.com/location/Corner+Cafe.jpg",
    "accesses": [
        { "id": 123456, ... },
        ...
    ]
}

Search Locations

Retrieve data for multiple locations with a GET request to the endpoint.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/location?query=vancouver -H "Accept: application/json"

The search query can match the name or address of the location. Omitting the search query will return all locations. The response body will contain the location data as a JSON array:

[
    { "name": "Corner Cafe", ...  },
    { "name": "Another Cafe", ...  }
]

A search can also be performed by geographic coordinates:

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/location?latitude=9.2839043&longitude=-123.1053527&minimumHours=140 -H "Accept: application/json"
latitude
Required. The latitude around which to search for nearby locations.
longitude
Required. The longitude around which to search for nearby locations.
minimumHours
The minimum number of hours the location must be open every week to be returned.

The results will be sorted by distance, and each location object will include its distance in kilometers from the specified coordinates:

[
    { "name": "Corner Cafe", distance: 3, ...  },
    { "name": "Another Cafe", distance: 5, ...  }
]

A search can also be performed using a geographic bounding box:

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/location?northEastLat=9.2839043&northEastLng=-123.1053527&southWestLat=9.2839043&southWestLng=-123.1053527 -H "Accept: application/json"
northEastLat
Required. The latitude of the north east corner of the bounds.
northEastLng
Required. The longitude of the north east corner of the bounds.
southWestLat
Required. The latitude of the south west corner of the bounds.
southWestLng
Required. The longitude of the south west corner of the bounds.

A search can also be filtered to include only locations where the authenticated user has an access:

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/location?access=true -H "Accept: application/json"
country
Required. The country in which to search for locations.
postalCode
Required. The postal code in which to search for locations. In Canada and the UK, this matches the first 3 characters only.
minimumHours
The minimum number of hours the location must be open every week to be returned.

A search can also be performed by postal code:

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/location?country=Canada&postalCode=A1A1A1 -H "Accept: application/json"
country
Required. The country in which to search for locations.
postalCode
Required. The postal code in which to search for locations. In Canada and the UK, this matches the first 3 characters only.
minimumHours
The minimum number of hours the location must be open every week to be returned.
[
    { "name": "Corner Cafe", ...  },
    { "name": "Another Cafe", ...  }
]

Results are limited to 5 per page, with additional pages available via the Link response header:

Link: <https://www.keycafe.com/v0/location?query=vancouver&page=2>; rel="next", <https://www.keycafe.com/v0/location?query=vancouver&page=5>; rel="last"

Event ↓↑

Event information is made available via the RESTful endpoint: https://www.keycafe.com/v0/event

Retrieve Event

Retrieve data for a single event with a GET request to the endpoint with an ID.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/event/123456 -H "Accept: application/json"

The response body will contain the event's data as a JSON object:

{
    "id": 123456,
    "dateCreated" : "2016-07-21T12:45:21Z"
    "location" : {
        "id": 123456,
        "name": "Corner Cafe",
        "timezone": "America/Vancouver",
        "address": {
            "street": "123 Main St.",
            "unit": "1A",
            "city": "Vancouver",
            "stateProv": "BC",
            "country": "Canada",
            "postalCode": "A1A1A1",
            "latitude": 9.2839043,
            "longitude": -123.1053527
        }
    },
    "user" : {
        "email": "user@yourdomain.com",
        "mobile": "16042656073",
        "firstName": "Bob",
        "lastName": "Smith"
    },
    "key" : {
        "id": 123456,
        "name": "Bob's Home Key",
        "serialNumber": "XXXXX",
        "notes": "The cleaners come every Monday at 10:00",
        "pickupNote": "Don't mind the dog. He's harmless.",
        "address": {
            "street": "123 Main St.",
            "unit": "1A",
            "city": "Vancouver",
            "stateProv": "BC",
            "country": "Canada",
            "postalCode": "A1A1A1"
        }
    },
    "access" : {
        "id": 123456,
        "startDate": "2016-07-21T12:45:21Z",
        "endDate": "2016-07-23T12:45:21Z",
        "returnReminder": false
    },
    "type" : "PICKUP",
    "bin" : "A-1"
}

For privacy reasons, only the contact information (email/mobile) provided when the access was created will be returned in the user object.

dateCreated
The date and time of the event, in ISO 8601 format.
location
The location of the event. See the location section for field information.
user
The user who completed the event. See the user section for field information.
key
The key used during the event. See the key section for field information.
access
The access used during the event. See the access section for field information.
type
The event type (PICKUP, DROPOFF, ACCESS_CREATED, ACCESS_CANCELLED).
bin
The bin label of the evnet.

Search Events

Retrieve data for multiple events with a GET request to the endpoint.

curl -u 'test@yourdomain.com:{password}' https://www.keycafe.com/v0/event?key=123456&user=123456&location=123456&type=PICKUP&before=2016-08-21T12:45:21Z&after=2016-07-21T12:45:21Z -H "Accept: application/json"

Omitting all of the following search properties will return all events.

key
The ID of a key by which to filter results.
user
The ID of a user by which to filter results.
location
The ID of a location by which to filter results.
type
The event type (PICKUP/DROPOFF/ACCESS_CREATED/ACCESS_CANCELLED) by which to filter results.
before
The latest possible date by which to filter results.
after
The earliest possible date by which to filter results.

The results will be sorted by date and time, descending:

[
    { "id": 123456, "dateCreated" : "2016-07-21T12:45:21Z", "type" : "PICKUP", ...  },
    { "id": 654321, "dateCreated" : "2016-07-21T15:45:21Z", "type" : "DROPOFF", ...  }
]

Results are limited to 50 per page, with additional pages available via the Link response header:

Link: <https://www.keycafe.com/v0/event?key=123456&page=2>; rel="next", <https://www.keycafe.com/v0/event?key=123456&page=5>; rel="last"

Notifications

To receive event notifications, configure a webhook endpoint URL and Basic Auth credentials in your account settings.

When a notification is received, respond with status code 200. If any other response code or error is encountered, Keycafe will try to deliver the notification again (up to 10 attempts) using exponential back-off to increase the time between each attempt.

Notifications are delivered as POST requests to the configured endpoint. Each POST body contains a single event in the same JSON format as above. Only exchange events (PICKUP/DROPOFF) for the user's own keys are sent as notifications.