Locker Open#
How to use locker open. Our lightweight app clip#
Locker Open is an App clip that is our easiest way to open a locker. Simply tap a link:
- And hit Open app clip to use Locker Open and pop the locker open!
- You can also follow the link at the bottom to the app store to download the full Locker Open app.

Using the App Clip#
Grant permissions to the app#
This step is needed for dropoff or pickup, it only needs to be done once.

Locker has opened#
The locker should open now ready for your drop off, put your item in and be sure to close the locker. Now your dropoff is complete!
Creating the 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#
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.
Determine the correct location using our location endpoints. Useful endpoints: locations
Choose the appropriate locker type based on the required space. Endpoint docs: locker types
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. These parameters are required in the api request:requireLowLocker
: Set to true if the user requires a low locker (e.g., for accessibility). TRUE/FALSEreturnUrl
: 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.
The response will include: -
id
: The locker open request ID -locker_id
-linkToken
: The URL token for the dropoff experience. The URL is what you will send to the customer
Creating a Pickup link
Make a POST request to
/v1/locker-open-request/pickup-locker-request
to generate a pickup link. Provide the following 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. 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.
The response format is the same as the dropoff link.
Harbor Webhooks#
Webhooks allow Harbor to notify your server when important events occur in real time.
To configure webhooks correctly, your system must provide a publicly accessible HTTPS endpoint where you wish to receive these notifications. Once our team registers the endpoint, you will begin receiving webhook events automatically.
Below is an example of how Harbor webhook events are structured.
Locker Open Request for Dropoff#
Our service provider will generate a Locker Open Request for dropoff.

This will send a notification to the configured webhook of the service provider with information in this format:
{
"timestamp": "2025-04-01T13:02:37",
"event": "LOCKER_ASSIGNED",
"data": {
"timestamp": "2025-04-01T16:02:36.884285+00:00",
"locker_id": 9,
"locker_status": "rented",
"tower_id": "0000000000000001",
"source": "LOCKER_OPEN_LINK_CREATED",
"reservation_id": null,
"open_locker_token_id": null,
"keypad_code": null,
"client_info": null,
"locker_open_request_id": 56,
"locker_open_request_payload": {
"extra": "data",
"test": 123
}
}
}
Similarly, when the end user uses the link to perform the dropoff, another notification of the same type will be sent to the configured webhook:
One when the token to open the locker is created:
{
"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
}
}
Another when the token is finally used:
{
"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
}
}
}
Pickup Process#
The same process will be repeated during the pickup process,
The last event will be a LOCKER_UNASSIGNED
type,
cause after the pickup happens the locker is not assigned to your company anymore.
Verifying Webhook Signature (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.
To verify the signature, follow the steps below:
Extract the signature from the headers
The webhook request will include a header named
x-harbor-signature
. Its value has the following format:t=1746022058,v1=c0aec4...,v2=...
t
is the Unix timestamp.v1
,v2
, etc., are versioned signatures.
Extract the all the signature values for verification.
Generate your own signature using the shared secret
Use an HMAC function (available in most standard libraries) to compute a signature using the following inputs:
The shared secret between Harbor and your system.
Request Payload. payload should be a concatenated string of the timestamp and the request payload delimited by, removing and space character. It should look like: {timestamp},{payload}
Encoding method: hex.
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.
- Validator functions example
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); }