Skip to content

PIN Management#

Use the PIN management endpoint to load, update, or delete keypad PIN codes from a lock. Only Yale locks with built-in keypads, or August locks with an attached smart keypad support PIN management.

The PIN management process is an asynchronous one, so you need to use webhooks to track success and failure. Here's an overview.

  1. You call the endpoint to load a PIN on a lock for one of your users. Included is a URL you want the API to send webhooks to.
  2. The API immediately responds with success to your API call.
  3. The API sends a command to the lock to load the PIN.
  4. The lock responds to the API with success or failure.
  5. The API passes the response back to you in a webhook.
  6. You inspect the webhook to determine whether the PIN was loaded on the lock or not.
  7. If the PIN wasn't loaded, you inspect the error message in the webhook to determine whether to retry or not.

Note

When you call this endpoint, the API sends a command to the lock through the bridge. So the bridge must be plugged in and connected to WiFi.

Terminology#

PIN command#

A command that is sent from the API to the lock, telling the lock to load, update, or delete a single PIN.

Method and Path#

POST /locks/:lockID/pins

Loading a PIN#

Let's start the process of loading the PIN 8572 onto a lock. We do so by passing a PIN command with action:load.

POST /locks/1234567890ABCDEF1234567890ABCDEF/pins

x-august-api-key: <your API key>
x-august-access-token: <your user's access token>

{
  "commands": [{
    "action": "load",
    "pin": "8572",
    "accessType": "always",
    "firstName": "Albert",
    "lastName": "Einsten",
    "partnerUserID": "7c750589-9411-4108-8428-5e0bcdf383e5"
  }],
  "webhook": "<your webhook URL>"
}

All fields in the request body are required.

  • commands - The PIN commands to process with this request. You can load multiple PINs on a lock at once using this array.
  • action - This tells the API to load a new PIN, or update or delete an existing one.
  • pin - The numeric PIN that will be loaded onto the lock.
  • accessType - This tells the API the PIN should always work on the lock until you remove it. The access types section has a list of the other access types.
  • firstName - The first name of the user that will be associated with this PIN.
  • lastName - The last name of the user that will be associated with this PIN.
  • partnerUserID - An arbitrary string that you pass to the API that is associated with this PIN. Each PIN must have a unique partner user ID.
  • webhook - The URL the API will send webhooks to.

The API will immediately respond with HTTP status 202 and a response body.

{
  "transactionID": "d255f8dc-5764-42c7-9069-e94e8ed56c17",
  "completionTime": "2023-10-11T21:06:57.962Z"
}
  • transactionID: The transaction ID for this API call. This will be included in the webhooks sent to you.
  • completionTime: When the response to the API call was sent.

But remember, this response is sent before the PIN command is sent to the lock. You still need to listen for webhooks to determine if the PIN is eventually loaded onto the lock or not.

Webhooks#

The API sends webhooks after it gets a response from the lock. There are two types of webhooks.

Commit#

The API sends a commit webhook after the PIN command is processed on the lock.

Success#

This is an example of a commit webhook for a PIN command that succeeded.

{
  "timeStamp": 1697058271455,
  "step": "commit",
  "transactionID": "d255f8dc-5764-42c7-9069-e94e8ed56c17",
  "partnerUserID": "partnerUser55555",
  "otherUserID": "5a5299fc-8faf-4369-8b81-81b5699f1c09",
  "action": "load",
  "pin": "8572",
  "completedDateTime": "2023-10-11T21:04:31.447Z",
  "syncType": "credential",
  "attemptNumber": 1,
  "status": "success",
  "lockID": "1234567890ABCDEF1234567890ABCDEF"
}
  • timeStamp - When the API sent the webhook, in milliseconds since the epoch.
  • step - This is either commit or digest.
  • transactionID - For a single API call, all commit webhooks and the digest webhook will have the same transaction ID.
  • partnerUserID - The partner user ID you passed in the command for this PIN.
  • otherUserID - The user ID of the new user that is assigned to this PIN.
  • action - The action you passed in the command for this PIN.
  • pin - The PIN you passed in the command for this PIN.
  • completedDateTime - When the API received a response from the lock after it processed the PIN command, in date time string format.
  • syncType: This will be credential.
  • attemptNumber: The number of attempts it took the API to connect to the lock and execute the PIN command. This is a legacy field and will always be 1 because the API does not retry automatically. See the retry section for an explanation of when you should retry failed PIN commands.
  • status: This tells you whether the PIN command succeeded (success) or failed (failure).
  • lockID: The lock ID you passed in the API call.

Failure#

This is an example of a commit webhook for a PIN command that failed. For a description of all possible failures, check out the error codes page.

{
  "timeStamp": 1695850638664,
  "step": "commit",
  "transactionID": "aa90c2a0-4b35-4bcf-87f3-bdb330ed6d7c",
  "partnerUserID": "partnerUser2",
  "otherUserID": "72d3f475-af89-4ff2-a78b-f7da86bff1af",
  "action": "load",
  "pin": "8572",
  "completedDateTime": "2023-09-27T21:37:18.658Z",
  "syncType": "credential",
  "attemptNumber": 1,
  "status": "failure",
  "error": 560,
  "errorName": "ERRNO_DISCONNECT",
  "errorMessage": "Unexpected Disconnect",
  "lockID": "1234567890ABCDEF1234567890ABCDEF"
}
  • status: This tells you whether the PIN command succeeded (success) or failed (failure).
  • error: The error's code.
  • errorName: The error's name.
  • errorMessage: The error's description.

Digest#

The API sends a digest webhook after the commit webhook. This webhook gives a summary of whether the PIN command succeeded or failed. Because you can load PINs in batches, digest webhooks may contain the result of multiple PIN commands.

{
  "timeStamp": 1697058271465,
  "step": "digest",
  "message": "PinSyncComplete",
  "transactionID": "d255f8dc-5764-42c7-9069-e94e8ed56c17",
  "callingUserID": "411e9733-0f48-4118-bbab-9e5def74419d",
  "digest": {
    "success": [
      {
        "action": "load",
        "pin": "55555",
        "partnerUserID": "partnerUser55555",
        "commitDate": "2023-10-11T21:04:31.000Z"
      }
    ],
    "conflict": [],
    "error": []
  },
  "commandsProcessed": 1,
  "requestTime": 1697058267953,
  "completionTime": 1697058271461,
  "lockID": "1234567890ABCDEF1234567890ABCDEF"
}
  • timeStamp - When the API sent the webhook, in milliseconds since the epoch.
  • step - This is either commit or digest.
  • message - This will be PinSyncComplete if all PIN commands are processed successfully. Otherwise, it will be PinSyncFail.
  • transactionID - For a single API call, all commit webhooks and the digest webhook will have the same transaction ID.
  • callingUserID - The user ID of the user who called the endpoint.
  • digest - The result of processing each PIN command. See the commit webhook for a description of the fields. The only new field in this webhook is commitDate, which is when you called the endpoint, in milliseconds since the epoch. It will be approximately the same for all PIN commands.
    • success - The PIN commands that were successfully processed.
    • conflict - The PIN commands that were not successfully processed.
    • error - The PIN commands that were not successfully processed. For legacy reasons, the API returns errors split into both the conflict and error arrays.
  • commandsProcessed - The number of PIN commands that were processed.
  • requestTime - When you called the endpoint, in milliseconds since the epoch.
  • completionTime: When the API received a response from the lock after it processed all the PIN commands, in milliseconds since the epoch.
  • lockID: The lock ID you passed in the API call.

Retrying#

You've called the endpoint, received the webhooks, and inspected the result of the PIN command. But it failed. Do you retry it? The answer is maybe.

Some errors should be retried after a known amount of time, like the 'bridge in use' error. Bridges can only process one command at a time, so the API will return this error if there is already another command in flight when you call the endpoint. In this case, you should retry the same PIN command after a short amount of time (how short?).

Some errors should be retried after an unknown amount of time, like the 'bridge offline' error. Then you should retry the same PIN command whenever you get the 'bridge online' webhook.

Some errors should not be retried, like the 'PIN already exists' error. Then you should choose another PIN in a new API call.

Check out the error codes page to see which errors are retryable and which are not.

Updating and Deleting PINs#

PINs that are already loaded on a lock can be updated or deleted. Note that some of the fields from the existing PIN are required for updates and deletes.

Let's say we want to change a PIN from 8572 to 0983. We do so by passing a PIN command with action:update. Here we pass the existing accessType and partnerUserID, and change the pin.

POST /locks/1234567890ABCDEF1234567890ABCDEF/pins

x-august-api-key: <your API key>
x-august-access-token: <your user's access token>

{
  "commands": [{
    "action": "update",
    "pin": "0983",
    "accessType": "always",
    "partnerUserID": "7c750589-9411-4108-8428-5e0bcdf383e5"
  }],
  "webhook": "<your webhook URL>"
}

We can also update the access type and/or access schedule of an existing PIN. This example changes a PIN from always being usable to only being usable from Monday to Friday, 9:00 am to 5:00 pm (what timezone?). Here we pass the existing pin and partnerUserID, and change the accessType.

POST /locks/1234567890ABCDEF1234567890ABCDEF/pins

x-august-api-key: <your API key>
x-august-access-token: <your user's access token>

{
  "commands": [{
    "action": "update",
    "pin": "8572",
    "accessType": "recurring",
    "accessTimes": "STARTSEC=32400;ENDSEC=61200",
    "accessRecurrence": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
    "partnerUserID": "7c750589-9411-4108-8428-5e0bcdf383e5"
  }],
  "webhook": "<your webhook URL>"
}

To delete a PIN, use action:delete.

POST /locks/1234567890ABCDEF1234567890ABCDEF/pins

x-august-api-key: <your API key>
x-august-access-token: <your user's access token>

{
  "commands": [{
    "action": "delete",
    "pin": "8572",
    "accessType": "always",
    "partnerUserID": "7c750589-9411-4108-8428-5e0bcdf383e5"
  }],
  "webhook": "<your webhook URL>"
}

Batch PIN Management#

All the examples so far have used one PIN command, but it's possible to pass multiple PIN commands to the endpoint. The API will execute them one at a time, sending commit webhooks as it gets the results from the lock. It sends the digest webhook after all PIN commands are processed.

Access Types#

The API supports PINs with three different access types. You control which access type a PIN uses with the accessType, accessTimes, and accessRecurrence fields in the PIN command.

Always#

An always PIN will work all day, every day, until you delete it.

When creating an always PIN, only accessType is required.

POST /locks/1234567890ABCDEF1234567890ABCDEF/pins

x-august-api-key: <your API key>
x-august-access-token: <your user's access token>

{
  "commands": [{
    "action": "load",
    "pin": "8572",
    "accessType": "always",
    "firstName": "Albert",
    "lastName": "Einsten",
    "partnerUserID": "7c750589-9411-4108-8428-5e0bcdf383e5"
  }],
  "webhook": "<your webhook URL>"
}

Recurring#

A recurring PIN will work only at certain times on certain days. For example, you can create a recurring PIN for a dog walker that only works from Monday to Friday, 9:00 am to 12:00 pm. The PIN will continue to work on these days and times until you delete it.

When creating a recurring PIN, accessType, accessTimes, and accessRecurrence are required.

POST /locks/1234567890ABCDEF1234567890ABCDEF/pins

x-august-api-key: <your API key>
x-august-access-token: <your user's access token>

{
  "commands": [{
    "action": "update",
    "pin": "8572",
    "accessType": "recurring",
    "accessTimes": "STARTSEC=32400;ENDSEC=43200",
    "accessRecurrence": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
    "firstName": "Dog",
    "lastName": "Walker",
    "partnerUserID": "7c750589-9411-4108-8428-5e0bcdf383e5"
  }],
  "webhook": "<your webhook URL>"
}
  • accessTimes - When creating a recurring PIN, this field specifies the time of day the PIN will start and stop working. It is specified in a custom format.
    • STARTSEC - The time of the day the PIN will start working, in seconds since 12:00 am.
    • ENDSEC - The time of the day the PIN will stop working, in seconds since 12:00 am.
  • accessRecurrence - The days of the week the PIN will work, in recurrence rule format.

Temporary#

A temporary PIN will work during a single window of time. For example, you can create a temporary PIN for a friend that works only from January 1st, 2024 at 5:00 pm to January 3rd, 2024 at 5:00 pm. You can delete a temporary PIN yourself, but the API will automatically delete it from the lock 7 days after it expires.

When creating a temporary PIN, accessType and accessTimes are required.

POST /locks/1234567890ABCDEF1234567890ABCDEF/pins

x-august-api-key: <your API key>
x-august-access-token: <your user's access token>

{
  "commands": [{
    "action": "update",
    "pin": "8572",
    "accessType": "temporary",
    "accessTimes": "DTSTART=2024-01-01T17:00:00.000Z;DTEND=2024-01-03T17:00:00.000Z",
    "firstName": "Your",
    "lastName": "Friend",
    "partnerUserID": "7c750589-9411-4108-8428-5e0bcdf383e5"
  }],
  "webhook": "<your webhook URL>"
}
  • accessTimes - When creating a temporary PIN, this field specifies the date and time the PIN will start and stop working.

Dedicated Master/Provisioning Code Management (bridge required)#

GET /locks/:lockID/masterpin

{
    "_id": "66d8ed5658c799ddca36b33f",
    "type": "pin",
    "lockID": "E706B426D4154AB7A056A78C1E9FCBF4",
    "slot": 65518,
    "userID": "masterpin",
    "state": "loaded",
    "pin": "0000",
    "accessType": "always",
    "createdAt": "2024-09-04T23:35:40.983Z",
    "updatedAt": "2024-09-04T23:35:40.983Z"
}

PUT /locks/:lockID/masterpin with body:

{
  "pin": "12345",
  "webhook": "https://webhook.site/yaleServerDefault"
}

202 Success response#

{
  "timeStamp": 1725492941029,
  "step": "commit",
  "transactionID": "b1fc86e2-bf64-456f-b0d1-e7b7a6bcbcfd",
  "otherUserID": "masterpin",
  "action": "load",
  "pin": "12345",
  "completedDateTime": "2024-09-04T23:35:41.022Z",
  "syncType": "credential",
  "attemptNumber": 1,
  "status": "success",
  "lockID": "E706B426D4154AB7A056A78C1E9FCBF4"
}

202 Success Webhook#

{
  "timeStamp": 1725492941044,
  "step": "digest",
  "message": "PinSyncComplete",
  "transactionID": "b1fc86e2-bf64-456f-b0d1-e7b7a6bcbcfd",
  "callingUserID": "a098bbca-d0be-44c6-96fc-2ae48a18cb21",
  "digest": {
    "success": [
      {
        "action": "load",
        "pin": "12345",
        "commitDate": "2024-09-04T23:35:41.000Z"
      }
    ],
    "conflict": [],
    "error": []
  },
  "commandsProcessed": 1,
  "requestTime": 1725492936273,
  "completionTime": 1725492941036,
  "lockID": "E706B426D4154AB7A056A78C1E9FCBF4"
}

Error Response#

bridge is powered, batteries are removed from the lock

{
  "timeStamp": 1725495355592,
  "step": "commit",
  "transactionID": "3cd68ad1-2c27-49d4-af09-6e1bbd050ac9",
  "otherUserID": "masterpin",
  "action": "load",
  "pin": "12345",
  "completedDateTime": "2024-09-05T00:15:55.584Z",
  "syncType": "credential",
  "attemptNumber": 1,
  "status": "conflict",
  "error": 408,
  "errorName": "ERRNO_LOCK_COMMAND_TIMEOUT",
  "errorMessage": "LockCommandTimeout",
  "lockID": "E706B426D4154AB7A056A78C1E9FCBF4"
}

Error Webhook#

bridge is powered, batteries are removed from the lock

{
  "timeStamp": 1725495355608,
  "step": "digest",
  "message": "PinSyncFail",
  "transactionID": "3cd68ad1-2c27-49d4-af09-6e1bbd050ac9",
  "callingUserID": "a098bbca-d0be-44c6-96fc-2ae48a18cb21",
  "digest": {
    "success": [],
    "conflict": [
      {
        "state": "commitFailed",
        "action": "load",
        "reason": "Unable to set commit state for pin. RBS error: 'LockCommandTimeout'. (Check RBS logs for more details)",
        "error": 408,
        "errorType": "rbs",
        "errorName": "ERRNO_LOCK_COMMAND_TIMEOUT"
      }
    ],
    "error": []
  },
  "commandsProcessed": 1,
  "requestTime": 1725495139092,
  "completionTime": 1725495355602,
  "lockID": "E706B426D4154AB7A056A78C1E9FCBF4"
}

FAQ#

How many PIN commands can I include in each request to the endpoint?

How many PINs can a lock hold? Use the capabilities endpoint to get the min and max PIN slot for the lock.

GET /devices/capabilities?deviceType=lock&deviceID=1234567890ABCDEF1234567890ABCDEF

{
  "lock": {
    "pinSlotMin": 1,
    "pinSlotMax": 500
  }
}

Subtracting pinSlotMin from pinSlotMax, then adding 1, tells you how many PINs a lock can hold. So in this example, the lock can hold 500 PINs.

Is the update action truly atomic?