Locker Open#
What is Locker Open?#
Locker Open is our lightweight app clip that provides the easiest way to open a Harbor locker. Simply tap a link and hit Open app clip to use Locker Open and pop the locker open!

How to Use Locker Open#
Step 1: Grant Permissions#
This step is needed for dropoff or pickup, and it only needs to be done once.

Step 2: Connect to the Tower#
Stay within 10 feet (2m) of the locker while the app is trying to connect.

Step 3: Complete Your Transaction#
For Dropoff:#
The locker will open automatically when you’re ready for dropoff
Place your item inside the locker
Important: Be sure to close the locker door
Your dropoff is now complete!

For Pickup:#
Use the link that was provided to you
The locker will open automatically
Remove your item from the locker
Important: Be sure to close the locker door



For Developers: Creating Universal Links#
You can generate universal links for your users by making API calls to Harbor’s endpoints from your server.
Creating a Dropoff Link#
- Step 1: Get Access Token
Request an access token. | If you are using the API docs to test this out, you will need to input your client and secret in the top right corner.
- Step 2: Find Your Location
Determine the correct location using our location endpoints. | Useful endpoints: locations
- Step 3: Choose Locker Type
Choose the appropriate locker type based on the required space. | Endpoint docs: locker types
- Step 4: Generate Dropoff Link
Make a POST request to
/v1/locker-open-request/dropoff-locker-request
to generate a dropoff link. | Use thelocationId
andlockerTypeId
obtained from the previous steps.Required Parameters:
requireLowLocker
: Set to true if the user requires a low locker (e.g., for accessibility).returnUrl
: The URL to redirect the user after completing the locker interaction. Example: “https://yourbrand.com/drop-off-success-page” Leave blank “” if you do not want a redirect.clientInfo
: A custom identifier (example: a user ID or transaction ID) that will be echoed back in webhook events. Leave blank “” if you don’t need an identifier.payload
: A custom JSON object containing additional metadata for your internal use. Leave blank {} if not needed.
Response includes:
id
: The locker open request IDlocker_id
linkToken
: The URL token for the dropoff experience. This is the URL you will send to the customer
Locker Open Request for Dropoff Diagram#
Here’s a diagram illustrating the process of creating the dropoff link.

Creating a Pickup Link#
- Step 1: Generate Pickup Link
Make a POST request to
/v1/locker-open-request/pickup-locker-request
to generate a pickup link.Required Parameters:
lockerId
: The ID of the locker from which the user will retrieve an item.returnUrl
: The URL to redirect the user after completing the pickup. Example: https://yourbrand.com/pickup-success-page Leave blank “” if you do not want a redirect.clientInfo
: A custom identifier to receive in webhook events. Leave blank “” if you don’t need an identifier.payload
: A custom JSON object to associate additional data with the transaction. Leave blank {} if not needed.
Response format: Same as the dropoff link.
Locker Open Request for Pickup Diagram#
Here’s a diagram illustrating the process of creating the pickup link.

Webhooks: Real-Time Notifications#
Webhooks allow Harbor to notify your server when important events occur in real time.
Setup Requirements: - Your system must provide a publicly accessible HTTPS endpoint. Once our team registers the endpoint, you will begin receiving webhook events automatically
Webhook Event Examples#
When a dropoff link is created:
When the user creates a token to open the locker:
{ "timestamp": "2025-04-01T13:12:57", "event": "LOCKER_OPERATION", "data": { "timestamp": "2025-04-01T16:12:57.521044+00:00", "locker_id": 9, "locker_status": "rented", "tower_id": "0000000000000001", "source": "LOCKER_TOKEN_CREATED", "reservation_id": null, "open_locker_token_id": 52, "keypad_code": null, "client_info": "Luis", "locker_open_request_id": null, "locker_open_request_payload": null } }
When the token is used to open the locker:
{ "timestamp": "2025-04-01T13:15:47", "event": "LOCKER_OPERATION", "data": { "timestamp": "2025-04-01T16:15:45+00:00", "locker_id": 9, "locker_status": "occupied", "tower_id": "0000000000000001", "source": "LOCKER_TOKEN_USED", "reservation_id": null, "open_locker_token_id": 52, "keypad_code": null, "client_info": "Luis", "locker_open_request_id": 56, "locker_open_request_payload": { "extra": "data", "test": 123 } } }
Verifying Webhook Signatures (Optional)#
Harbor and your system can share a secret used to verify the signatures of incoming webhook requests. This step is optional but recommended as an additional security measure.
- Step 1: Extract the Signature
The webhook request will include a header named
x-harbor-signature
with this format: |t=1746022058,v1=c0aec4...,v2=...
-t
is the Unix timestamp -v1
,v2
, etc., are versioned signatures- Step 2: Generate Your Own Signature
Use an HMAC function to compute a signature using: - The shared secret between Harbor and your system - Request payload: concatenated string of timestamp and request payload delimited by comma, removing all space characters - Format: {timestamp},{payload} - Encoding method: hex
- Step 3: Compare Signatures
Compare your computed signature to the ones provided in the
x-harbor-signature
header. The computed signature must match at least one of the provided signatures (e.g.,v1
) for the verification to pass.
Example Validator Functions:
import re
import hmac
import hashlib
def is_signature_valid(
x_harbor_signature: str, payload: str, shared_secret: str
) -> bool:
"""Determines if harbor signature is valid or not"""
matches = dict(re.findall(r"(\w+)=([^,]+)", header))
timestamp = matches.get("t")
signatures = [v for k, v in matches.items() if k.startswith("v")]
compact_payload = payload.replace(" ", "")
signed_content = f"{timestamp},{compact_payload}"
generated_signature = hmac.new(
shared_secret.encode("utf-8"),
signed_content.encode("utf-8"),
hashlib.sha256,
).hexdigest()
return generated_signature in signatures
/**
* Verifies a Harbor-style HMAC signature
* @param {string} xHarborSignature - The full header string (e.g. "t=...,v1=...,v2=...")
* @param {string} payload - The raw request body as a string
* @param {string} sharedSecret - The shared HMAC secret
* @returns {boolean}
*/
function isSignatureValid(xHarborSignature, payload, sharedSecret) {
const matches = Object.fromEntries(
[...xHarborSignature.matchAll(/(\w+)=([^,]+)/g)].map(match => [match[1], match[2]])
);
const timestamp = matches["t"];
const signatures = Object.entries(matches)
.filter(([k]) => k.startsWith("v"))
.map(([, v]) => v);
if (!timestamp || signatures.length === 0) return false;
const compactPayload = payload.replace(/\s+/g, "");
const signedContent = `${timestamp},${compactPayload}`;
const generatedSignature = crypto
.createHmac("sha256", sharedSecret)
.update(signedContent, "utf8")
.digest("hex");
return signatures.includes(generatedSignature);
}