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 augustUserID is a phone number then it must be specified according to RFC3966 (the tel URI for Telephone Numbers).
  • that each input goes through a format and value validation.
  • that for a temporary-type the accessTimes field is provided and valid (we use jsical to parse).
  • that for a recurring type the accessTimes and accessRecurrent are provided and valid.
  • that the webhook url is valid and https.
    • The callbacks will be executed over https,
    • the certificate should be valid.
  • 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 onetime PIN 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.