Remote PIN Management for the August Keypad
These endpoints are for managing keypad PIN codes via the API.
Check Lock Type
GET /locks/:lockID
You'll want to look at the Type field. Type 1 locks are the original August Smart Locks, and they can only handle accessType: "always". Newer Type locks (like Type: 2) can accept recurring and temporary PINs.
Request Valid PIN
GET /locks/:lockID/pin
This will generate and reserve a randomly-generated PIN for you. It will be reserved for several minutes (currently 3 minutes). You may call this several times to generate and reserve several PINs. No more than 240 PINs can be set or reserved for a specific lock.
PIN:User is 1:1
For each lock, the relationship of PIN:User is 1:1. That is, you cannot assign the same PIN to two different users on the same lock.
HTTP 409 Error
Attempts to set the same PIN for a second user will result in an HTTP 409 error. Similarly, if you try to set a "new" pin for a user that already has a PIN, you will also see an HTTP 409 error.
Changing a User's PIN
To change a user's PIN you must first delete their current PIN and then load the new PIN. Do this in two separate transactions, waiting for the first transaction to complete, to avoid race conditions in the database. We consider this potential race condition to be a bug (internal ID PART-615) but it will be complex to fix and we do not have an ETA yet. Race conditions will lead to Unable to set intent state for pin errors.
Setting New PINs via the REST API
POST /locks/:lockID/pins
This endpoint takes an array of commands so that you can configure multiple PINs on the same lock at the same time. The commands will be run in the order they appear in the array of commands. For example, if one of your users already has a PIN and you want to update or change the PIN, you must first delete it (including an augustUserID) and then load it again. You can do this in the same POST call with two commands in the same array.
It is cleaner if you do not provide an augustUserID to load a PIN because then the August API will automatically create an unverified user ID. Unverified users are a special class of users just for PIN access. They do not get invited to use the August app and cannot use their phones to operate the lock. August does not verify the email addresses or phone numbers of unverified users. You can use this id any place we request an augustUserID and our servers will know the type of user.
If you do provide an augustUserID in a command, then the API will try to match
the id to all registered August users. If there is a match, the API will assign
the PIN to that existing user. If they already have a PIN set on that lock,
you'll get an HTTP 409 error. If the PIN is set successfully and that user has
the August Application, they will see the PIN. If the user is not a verified
August user, then they will not receive any indication of the PIN -- in that
case, communicating the PIN is up to you.
For registered users (those using the August app), August has verified the
phone number and email address, so we know they are all real. Please do not
generate a "fake" telephone number or email address because you run the risk
that a real August user will see the PIN in the August application. If you need
a real August user UUID then you can get it from the list of PINs. We hope in
the future to be able to offer delete with the partnerUserID but that is not
ready yet, so you must use the August user UUID associated with the PIN code.
POST /locks/:LockID/pins Payload Example
augustUserID No Longer Required
augustUserID is no longer required!
You can load and delete using the partnerUserID.
If you provide the augustUserID, ensure that it is a
valid August user UUID or a real phone number or email address.
{
"commands: [{
"partnerUserID": "uid", /* string: REQUIRED user id which will be used to report upon completion of each pin command */
"pin": "pin", /* string: REQUIRED to load a valid pin, 4-6 digits, ideally obtained with GET locks/:LockID/pin to avoid failures */
"action": "action", /* string: REQUIRED load, delete, disable, enable */
"accessType": "type" /* string: REQUIRED the type of the pin: always, recurring, temporary, onetime */
"augustUserID": "augustID", /* string: OPTIONAL if partnerUserID provided, either a phone number (tel:...) or an august internal ID */
"firstName": "firstName", /* string: OPTIONAL the firstName of the user, shows up in lock owner's August App */
"lastName": "lastName", /* string: OPTIONAL the lastName of the user, shows up in lock owner's August App */
"accessTimes": "accessTime" /* string: REQUIRED for accessType recurring and temporary. See examples below */
"accessRecurrence": "recurrence" /* string: REQUIRED for access type recurring. See examples below. */
"retry": true | false /* boolean: OPTIONAL if true and the PIN operation fails, the API will retry the PIN operation. Defaults to false. */
}],
"webhook": "url"
}
// Example accessTimes:
// For recurring access type possible values are:
"STARTSEC=<sec from start of day>[;ENDSEC=<sec from start of day>]";
// If timezone of lock is known this is also valid:
"DTSTART=<ISO date in UTC>[;DTEND=<ISO date in UTC>]"
// (end date optional, if not provided is set to 1 hour from start date).
// For temporary times must be ISO date in UTC.
onetime PINs
Locks that contain a "Connected by August" module (Yale, Emtek, etc.) do not support onetime PINs.
While there is not restriction on getting creating the PIN, we strongly
suggest to call GET /locks/:lockID/pin to receive a PIN without collision.
PINs are unique per lock and per user. Those GET /pins are set aside currently
for 3 minutes. Since we are doing batch processing we are considering to
increase this for partners.
Upon receiving the payload, after the server makes it's usual lock existence and lock ownership checks, it will check for the validity of the payload.
Examples of checks on the payload:
- if the
augustUserIDis a phone number then it must be specified according to RFC3966 (thetelURI for Telephone Numbers). - that each input goes through a format and value validation.
- that for a
temporary-type theaccessTimesfield is provided and valid (we usejsicalto parse). - that for a
recurringtype theaccessTimesandaccessRecurrentare provided and valid. - that the
webhookurl is valid andhttps.- The callbacks will be executed over
https, - the certificate should be valid.
- The callbacks will be executed over
- if a time-based rule is specified and the lock is an ASL-1 lock (first generation) then we will reject the rule because this lock cannot track time well.
tel: versus phone:
Some places in the August APIs we use tel: for the phone number
identifier, and other places we use phone:. The docs should say which to
use (as tel: above), but if they don't, please let us know and we will
update the docs.
load Examples
Note that the partnerUserID is a string you define. It must be unique to this
user and must not be reused for any other users by your company. (Internally we
are namespacing these by your OAuth clientID.)
{
"commands": [
{
"partnerUserID": "PINTESTALWAYS",
"firstName": "Test",
"lastName": "PINTOOLA",
"pin": "2358",
"action": "load",
"accessType": "always"
},
{
"partnerUserID": "PINTESTRECUR",
"firstName": "Test",
"lastName": "PINTOOLR",
"pin": "2359",
"action": "load",
"accessType": "recurring",
// This example starts at 1am and ends at 2am in the lock's time zone.
"accessTimes": "STARTSEC=3600;ENDSEC=7200",
"accessRecurrence": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR"
},
{
"partnerUserID": "PINTESTTEMP",
"firstName": "Test",
"lastName": "PINTOOLT",
"pin": "2360",
"action": "load",
"accessType": "temporary",
// This example starts at midnight on May 24 GMT and ends at the end of the day.
"accessTimes": "DTSTART=2017-05-24T00:00:00.000Z;DTEND=2017-05-24T23:59:59.000Z"
}
],
"webhook": "$WEBHOOK"
}
delete Examples
Note that the partnerUserID and accessType fields match the fields used in
the load Examples above.
{
"commands": [
{
"partnerUserID": "PINTESTALWAYS",
"action": "delete",
"accessType": "always"
},
{
"partnerUserID": "PINTESTRECUR",
"action": "delete",
"accessType": "recurring"
},
{
"partnerUserID": "PINTESTTEMP",
"action": "delete",
"accessType": "temporary"
}
],
"webhook": "$WEBHOOK"
}
Response
Success
If all is valid the system will respond with a 202 code, and the following payload:
Response Payload
{
status: 'success',
transactionID: tid, /*string: a uuid */
completionTime: cts, /*integer: an approximate completion time in seconds - optional*/
}
Keep the transactionID
That transactionID is important! The asynchronous callbacks and final
digest will all refer to that ID so that you know which request caused the
callbacks.
Failure and Error Codes
- 401 If the user is not allowed to perform the operation
- 404 If the lock doesn't exist
- 409 If the payload doesn't validate:
- duplicate PIN code,
- user already has a PIN code,
- payload validation includes checking that the type of lock can handle the access rules requested, e.g. ASL1 can't handle recurring rules but ASL2 can.
- trying to set a
onetimePIN on a "Connected by August" lock from Yale, Emtek, etc. - trying to set a PIN on a lock with unavailable slots
- 500 if there is some sort of internal error
Processing
POST :webhook
Successes and failures
Successes and failures are reported via the webhook, asynchronously.
August processes each PIN request in the commands array separately and in
order. If during any of the internal steps to fulfill a command fails, then
August will call the partner-specified webhook with an error code and a
message specifying the error.
The webhook will be called only once for a successful command.
When all the transactions have been completed with or without failures, the system will call back the webhook with a digest.
Success Payload
{
"step": "commit",
"status": "success",
"transactionID": transactionID, /* the transaction id generated by the system */
"partnerUserID": partnerUserID, /* the partner provided user identifier */
"action": action, /* the action you provided when calling the endpoint */
"pin": pin, /* the pin that failed */
"completedDateTime": completedDateTime, /* Date time in ISO 8601 format */
"syncType": "credential",
}
Conflict Payload
{
"status": "conflict",
"transactionID": transactionID, /* the transaction id generated by the system */
"partnerUserID": partnerUserID, /* the partner provided user identifier */
"action": action, /* the action you provided when calling the endpoint */
"pin": pin, /* the pin that failed */
"syncType": "credential",
"completedDateTime": completedDateTime, /* Date time in ISO 8601 format */
"error": error, /* the HTTP status code returned by our internal PIN processing service, which will be in the 400-599 range */
"errorName": errorName /* the name of the error */,
"errorMessage": /* A human readable message explaining the nature of the conflict error */
}
Failure Payload
{
"status": "failure",
"transactionID": transactionID, /* the transaction id generated by the system */
"partnerUserID": partnerUserID, /* the partner provided user identifier */
"action": action, /* the action you provided when calling the endpoint `POST /"locks"/:lockID/pins` endpoint */
"pin": pin, /* the pin that failed */
"syncType": "credential",
"completedDateTime": completedDateTime, /* Date time in ISO 8601 format */
"error": error, /* the HTTP status code returned by our internal PIN processing service, which will be in the 400-599 range */
"errorName": errorName /* the name of the error */,
"errorMessage": errorMessage /* A human readable message explaining the nature of the conflict error */
}
Digest
When August has completed processing of all your PIN requests from your original POST /locks/:lockID/pins, we will send you a digest.
The processor retrieves all the records from your transaction and creates a response object of the following format:
Final Response
{
"step": "digest",
"message": message, /* PinSyncFail on failure, PinSyncComplete on success */
"transactionID": transactionID,
"callingUserID": callingUserID, /* ID of the user who called the `POST /locks/:lockID/pins` endpoint. */
"digest": {
"success": [{
"partnerUserID": partnerUserID, /* the partner provided user identifier */
"commitDate": commitDate, /* Date time in ISO 8601 format */
"action": action, /* the action you provided when calling the endpoint */
"pin": pin
}],
"conflict": [{
"state": "commitFailed",
"action": action, /* the action you provided when calling the endpoint */
"partnerUserID": partnerUserID, /* the partner provided user identifier */
"reason": reason, /* A human readable message explaining the nature of the conflict error */
"error": error, /* the HTTP status code returned by our internal PIN processing service, which will be in the 400-599 range */
"errorType": "rbs",
"errorName": errorName /* the name of the error */
}],
"error": [{
"state": "commitFailed",
"action": action, /* the action you provided when calling the endpoint */
"partnerUserID": partnerUserID, /* the partner provided user identifier */
"reason": reason, /* A human readable message explaining the nature of the conflict error */
"error": error, /* the HTTP status code returned by our internal PIN processing service, which will be in the 400-599 range */
"errorType": "rbs",
"errorName": errorName /* the name of the error */,
}]
},
"commandsProcessed": numCommands, /* The number of PINs that the API tried to process */
"requestTime": requestTime, /* Epoch timestamp */
"completionTime": completionTime /* Epoch timestamp */
}
Recurring PINs
Recurring PINs have only one recurrence pattern: weekly. That is, whatever times and dates you give access will repeat each week.
Generating Rules
Try out the rrule.js demo to generate
weekly rules. We use this library internally for parsing the
accessRecurrence string. Note that accessRecurrence tells us when to
apply accessTimes.
Example recurring payload.
Here is what you might set if you wanted to allow your guitar teacher in to your house on every Tuesday and Thursdays any time between 9am and 2pm:
{
"commands": [
{
"partnerUserID": "teacherIDxyz",
"firstName": "Guitar",
"lastName": "Hero",
"pin": "12345",
"action": "load",
"accessType": "recurring",
"accessTimes": "STARTSEC=32400;ENDSEC=50400",
"accessRecurrence": "FREQ=WEEKLY;BYDAY=TU,TH"
}
],
"webhook": "https://example.com/callback/1jzsz7e1"
}
Example temporary payload
Here is what you would use to give Santa Claus access on Christmas Eve 9pm to 3am Christmas Day:
{
"commands": [
{
"partnerUserID": "HoHoHo",
"firstName": "Santa",
"lastName": "Claus",
"pin": "122425",
"action": "load",
"accessType": "temporary",
"accessTimes": "DTSTART=2016-12-25T05:00:00.000Z;DTEND=2016-12-25T11:00:00.000Z"
}
],
"webhook": "https://example.com/callback/1jzsz7e1"
}
Lock Time Zones
This example assumes that the lock timezone has been set to PST. The lock time zone gets set by the August app during setup, based on the timezone of the phone at that moment.