Webhooks
On This Page
Webhooks are a way for your servers to be notified of events that happen on August locks, doorbells and users.
Webhook Types
Locks POST /webhook/:lockID
- operation:
- detect when the lock is locked, unlocked, or its status is checked by anyone (so you always know the same state as all other checkers)
- detect when the door is opened or closed for locks with a DoorSense™ sensor.
- configuration: detect when the name of a lock changes.
- battery: detect when batteries run low in locks and keypads.
- systemstatus: detect when a lock is online of offline (i.e. reachable remotely)
- accessmgmt: Not implemented yet. Detect role changes of user (e.g. guest to owner).
Doorbells POST /webhook/doorbell/:doorbellID
- motiondetected : notification of motion detected by doorbell.
- videoavailable : notification of doorbell video available.
- buttonpush : notification of doorbell button pushed.
Users POST /webhook/user
- lockmembership : Receive notification when this user is added to or removed from a lock.
No UserID in /user URL
We don't include the UserID in the URL because we can determine it from the access token.
Using Webhooks
All types of webhooks (locks, doorbells, and users) work with the same pattern:
- You set up an internet-facing service which can receive an HTTP
POST(an HTTPPUTis possible for some hooks, butPOSTis best). - You call an August API for each lock, doorbell, or user to tell us what events you want to hear and the URL to call with the event information for that lock.
- Time passes
- The event happens on the lock, doorbell or user.
- August makes one call to the endpoint you registered for that lock, doorbell, or user.
- When you no longer want or need the webhook, you call
DELETE /webhook/...as appropriate to delete your webhook registration.
Using the /webhook/ and /webhook/doorbell/ APIs, each lock or doorbell can
have one webhook registered per user/partner/device combination. If you call
/webhook for the same user and device more than one time, the values sent in
the last call will be the ones in effect.
Using the /webhook/user API, each user can have one webhook registered per
user/partner combination.
See Also 👓
Registering a Lock Webhook
Here is how to set a webhook for a specific lock. This webhook will get
callbacks for all the event types listed in the notificationTypes array. In
this example, we will subscribe to all the types.
Here we use cURL to register for the webhook. In your own API, you would POST
the data directly. Here the POST data is being passed in via text file named
webhook.json.
curl -X POST \
-d @webhook.json \
-H "x-august-api-key: API_Key_August_Gave_You" \
-H "x-august-access-token: validAccessToken_You_Got_from_OAuth" \
-H "content-type: application/json"\
https://api-production.august.com/webhook/:LockID \
--trace-ascii /dev/stdout
Contents of webhook.json file (which you sent with the @webhook.json above):
{
"url": "https://my.webhook.endpoint/",
"clientID":"client_id_given_by_August",
"header": "name_of_header_you_expect",
"token": "secret_you_create",
"method": "POST",
"notificationTypes": [
"operation",
"configuration",
"battery",
"systemstatus",
"accessmgmt"
]
}
Parameters You Provide
Note that there are two parameters which you create at the time of the call
to POST /webhook/:
header: the name of a header we will send in the callbacktoken: the value we will pass back to you via theheadername you gave us.
These should have meaning to your API -- this is a way you can be sure the call is coming from August.
The output of the example cURL will be something like this:
== Info: Trying 166.78.41.145...
== Info: Connected to api-production.august.com (166.78.41.145) port 443 (#0)
== Info: TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
== Info: Server certificate: *.august.com
== Info: Server certificate: DigiCert SHA2 Secure Server CA
== Info: Server certificate: DigiCert Global Root CA
=> Send header, 1024 bytes (0x400)
0000: POST /webhook/:LockID HTTP/1.1
0039: Host: api-production.august.com
005a: User-Agent: curl/7.43.0
0073: Accept: */*
0080: x-august-api-key: API_Key_August_Gave_You
00b8: x-august-access-token: validAccessToken_You_Got_from_OAuth
00f8: ...
03b8: ...
03c9: content-type: application/json
03e9: Content-Length: 280
03fe:
=> Send data, 280 bytes (0x118)
0000: {"url": "https://my.webhook.endpoint/", "client
0040: ID":"client_id_given_by_August", "header": "name_of_header_you_expect",
0080: "method": "POST", "notificationTypes": [ "operation",
00c0: "systemstatus", "battery", "accessmgmt", "configuration" ], "tok
0100: en": "secret_you_create"}
== Info: upload completely sent off: 280 out of 280 bytes
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 30 bytes (0x1e)
0000: Server: nginx/1.4.6 (Ubuntu)
<= Recv header, 37 bytes (0x25)
0000: Date: Fri, 05 Feb 2016 21:31:08 GMT
<= Recv header, 32 bytes (0x20)
0000: Content-Type: application/json
<= Recv header, 20 bytes (0x14)
0000: Content-Length: 21
<= Recv header, 24 bytes (0x18)
0000: Connection: keep-alive
<= Recv header, 785 bytes (0x311)
0000: x-august-access-token:
0040: ...
0300: ...
<= Recv header, 21 bytes (0x15)
0000: x-response-time: 10
<= Recv header, 2 bytes (0x2)
0000:
<= Recv data, 21 bytes (0x15)
0000: {"message":"success"}
== Info: Connection #0 to host api-production.august.com left intact
{"message":"success"}
Obviously you are really looking for that response in your app, {"message":"success"} when you register the webhook.
Note
You can register the same webhook for multiple locks, but you will have to call the API once for each lock. When we make the callback we will include the LockID in the payload so you can tell which lock had the event.
Warning
August will only call the webhook once for each event, and we will not follow any redirections (HTTP 302).
Check signatures
All webhooks have a signature in each event's X-August-Signature header. This allows you to verify that the events
were sent by August, not by a third party.
Preventing replay attacks
A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them.
To mitigate such attacks, August includes a timestamp in the X-August-Signature header.
Because this timestamp is part of the signed payload, it is also verified by the signature,
so an attacker cannot change the timestamp without invalidating the signature. If the signature is valid but
the timestamp is too old, you can have your application reject the payload. We recommend a default
tolerance of 5 minutes.
Verifying Signatures
August generates signatures using a hash-based message authentication code (HMAC) with SHA-256.
Step 1. Extract timestamp and signature from the header
Split the header, using the , character as the separator, to get a list of elements.
Then split each element, using the = character as the separator, to get a prefix and value pair.
The value for the prefix t corresponds to the timestamp, and v corresponds to the signature.
Step 2. Create the payload string
The payload_string is created by concatenating the following:
timestampas a string- the character
. - The stringified JSON payload (the request body).
Step 3. Generate the expected signature
Compute an HMAC with the SHA256 hash function. Using your API key as a signing secret and
the payload_string as the message.
Step 4. Compare the expected and actual signature
Compare the signature in the header to the expected signature. Compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.
Example Webhooks
operation (lock or unlock)
The lock is locked or unlocked.
with August app or API
The lock was operated with the August app or the API.
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"LockID": ":LockID", // the same :LockID you used to register the webhook
"EventType": "operation",
"Event": "unlock", // could also be "lock"
"Device": "lock",
"User": { // information about the user that cause the event
"UserID": ":userID", // the August user identifier
"FirstName": "Andy",
"LastName": "Rothfusz",
"PhoneNo": "+15555555555", // user's verified phone number
"Email": "user@example.com", // user's verified email address
"imageInfo": null // this could include URLs to unverified images of the user
}
}
with Keypad
The lock was operated via the keypad.
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"LockID": ":LockID", // the same :LockID you used to register the webhook
"EventType": "operation",
"Event": "unlock", // could also be "lock"
"Device": "keypad",
"User": { // information about the user that cause the event
"UserID": ":userID", // the August user identifier
"FirstName": "Andy",
"LastName": "Rothfusz",
"PhoneNo": "+15555555555", // user's verified phone number
"Email": "user@example.com", // user's verified email address
"imageInfo": null // this could include URLs to unverified images of the user
}
}
manually
Someone turned the lock manually.
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"LockID": ":LockID", // the same :LockID you used to register the webhook
"Device": "keypad",
"EventType": "operation",
"Event": "unlock", // could also be "lock"
"User": { // information about the user that cause the event
"UserID": "manualunlock", // could also be "manuallock"
}
}
operation (door opened or closed)
The door is opened or closed (for locks with a DoorSense™ sensor).
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"LockID": ":LockID", // the same :LockID you used to register the webhook
"EventType": "operation",
"Event": "open", // could also be "closed" or "init" or "unknown"
"Device": "lock",
"User": { // information about the user that cause the event
"UserID": "DoorStateChanged"
}
}
operation (status)
The Event tells you the state of the lock which the status check returned.
So operation webhooks will now send you two EventType: "operation" and
"status". The "operation" fires as soon as the actual event happens. The
"status" fires whenever anyone calls /remoteoperate/status and notifies all
listeners on that lock of the current status.
That way you can reduce the number of times you call status yourself. You
always know the latest state of the lock, at least as well as any other
listeners do.
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"LockID": "1A2B...",
"EventType": "status", // status means it is the current state, operation means it is a new state
"Event": "lock", // The current state of the lock
"Device": "lock",
"User": { // Which user checked the lock status
"UserID": "some-user-id",
"FirstName": "Some",
"LastName": "User",
"PhoneNo": "+15555555555",
"Email": "someone@example.com",
"imageInfo": {
"original": {
"width": 640,
"height": 480,
"format": "jpg",
"url": "https://example.com/some.jpg",
"secure_url": "https://example.com/some.jpg"
},
"thumbnail": {
// similar to 'original'
}
}
}
}
configuration
If you create a webhook requesting callbacks for configuration
changes, you will now
get notified of name changes. This can help you to refer to a lock in your
application by the same name the user sets in the August application.
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"LockID": "ABC123",
"EventType": "configuration",
"Event": "lock_name_changed",
"Lock": {
"Name": "new lock name"
},
"User": {
"UserID": "abcd-1234",
"FirstName": "John",
"LastName": "Smith",
"PhoneNo": "+165011122222",
"Email": "john@smith.com",
"imageInfo": {
// ...
}
}
}
systemstatus
It can take several minutes (~5) for the August servers to decide that a lock is offline, so this webhook will not fire immediately when the lock disconnects. However the server will send out a notification as soon as it sees the lock come back online.
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"EventType": "systemstatus",
"LockID": ["4F206CBE466645379CDF5F39FB992683"],
"Event": "online"
}
| Event | Meaning |
|---|---|
online |
The lock is online. |
offline |
The lock is offline. |
battery (keypad)
The battery webhook fires when the August servers learn that the batteries on a device are running low enough that the owner should be notified.
Keypads and locks can both run low on batteries.
Warning
This battery callback JSON will be changing to make it more consistent. See Future "Consistent" format below.
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
X-August-Signature: signature
Body
{
"EventType": "battery",
"LockID": "4F206CBE466645379CDF5F39FB992683",
"Event": "keypad_battery_critical",
"DeviceType": "keypad",
"DeviceSerialNumber": "K1GJW0004G"
}
| Event | Meaning |
|---|---|
keypad_battery_none |
no action required by owner |
keypad_battery_warning |
batteries are running low |
keypad_battery_critical |
batteries need replacement immediately or the user risks the keypad not working |
battery (lock)
HTTP Headers
Content-Length: 252
Connection: keep-alive
Name-Of-Header-You-Expect: secret_you_create
Content-Type: application/json
Body
{
"EventType": "system",
"LockID": "lock ID of lock with the battery event",
"Event": "lock_battery_alert",
"warningLevel": "lock_state_battery_warning_2week",
"userID": "the user ID who registered the webhook"
}
Event |
Meaning |
|---|---|
lock_state_battery_warning_none |
no action required by owner |
lock_state_battery_warning_2week |
batteries are running low, should last about 2 weeks |
lock_state_battery_warning_1week |
batteries should last about 1 week |
lock_state_battery_warning_2day |
batteries need replacement immediately or the user risks the lock not working |
Future "Consistent" format
We are working on a new structure for both types of battery warnings (internal issue id is ASL1-11815).
The current plan is for this format:
{
"EventType":"battery",
"DeviceType":"keypad|lock",
"LockID":"lockID or the lock ID associated with the keypad",
"UserID":"user ID of person who registered the hook",
"Event":"battery_level_none|battery_level_warning|battery_level_critical"
}
Example Response: motiondetected (/doorbell)
{
"DoorbellID":"54b6c08ed4c6",
"EventType":"doorbell_motion_detected",
"Url":"http://res.cloudinary.com/august-com/image/upload/v1545186381/bxdxlcgc00hioa98y1xw.jpg",
"SecureURL":"https://res.cloudinary.com/august-com/image/upload/v1545186381/bxdxlcgc00hioa98y1xw.jpg",
"Width":480,
"Height":640
}
The motion detected event includes a still image taken at the time of the motion.
Example Response: buttonpush (/doorbell)
{
"DoorbellID":"54b6c08ed4c6",
"EventType":"buttonpush",
"dvrID":"d865a29e-cbd6-4b80-8944-8935d217757e"
}
Example Response: videoavailable (/doorbell)
{
"DoorbellID":"54b6c08ed4c6",
"EventType":"doorbell_video_upload_available",
"dvrID":"5714bff4-5a94-4780-bda4-f94026e2a715",
"cause":"doorbell_motion_detected",
"startTime":1545187132470
}
To get the URL to the video, you must call GET /doorbells/:doorbellID/videoevent?dvrID=<dvrID> which will return
{
"url": "https://something.cloudfront.net/<uuid>/<doorbellid>/<uuid>/playlist.m3u8"
}
Example Response: lockmembership (/user)
// User was added to a lock
{
"UserID":"cfa91435-41b0-4632-877b-339dfaf4a445",
"EventType":"authorization",
"Event":"lock_user_add",
"LockID":"4F206CBE466645379CDF5F39FB992683"
}
// User was removed from a lock
{
"UserID":"cfa91435-41b0-4632-877b-339dfaf4a445",
"EventType":"authorization",
"Event":"lock_user_remove",
"LockID":"4F206CBE466645379CDF5F39FB992683"
}