Implementation Guide
Build Your Integration Service
Implement seven HTTPS endpoints that Sine can call. Your service authenticates each request, maps the KMI operation to your key management system (KMS), and returns the specified response.
The proposed configuration provides an endpoint URL for each operation. Paths in the examples are illustrative - your URLs do not have to use these paths. Once the framework is available, a Sine administrator would configure the URLs, an API key, an externalId and the selected key groups without requiring a bespoke Sine integration.
Operation | Example endpoint | Purpose | Proposed success status |
|---|---|---|---|
Verify | POST /kmi/verify | Validate the connection | 200 |
Resources | POST /kmi/resources | List key groups | 200 |
Keys | POST /kmi/keys | Return the selected groups' keys | 200 |
Provision | POST /kmi/provision | Prepare a cabinet user | 201 |
Activate | POST /kmi/activate | Enable requested key access | 200 |
Check-return | POST /kmi/check-return | Report outstanding requested keys | 200 |
Deprovision | POST /kmi/deprovision | Remove the visitor's cabinet access | 200 |
These are proposed HTTP statuses and response shapes, not evidence of an already supported public API. The final configuration, schemas, statuses, timeouts and retry policy require approval.
Transport and Authentication
For each request, the proposed framework sends:
POST /kmi/verify HTTP/1.1
Host: integration.example.com
Authorization: <REPLACE_ME>
Content-Type: application/json
Accept: application/jsonThe Authorization value is the KMI API key configured in Sine. This proposal sends the configured value as-is; it does not add a Bearer prefix unless that prefix is part of the configured value. Authenticate every endpoint, reject invalid credentials, and use HTTPS with a valid certificate.
Return successful and error responses as JSON with Content-Type: application/json.
Keep the KMI API key and your cabinet API credentials separate. Your service owns and securely stores the cabinet credentials; the proposed KMI payloads do not require Sine to send them.
Issue a separate KMI API key for each customer integration and bind it to the authorised externalId values. On every request, authenticate the key and check that it is authorised for the supplied config.externalId. Reject an invalid key with 401 and a valid key used with an unauthorised externalId with 403. Do not trust externalId alone to establish the customer.
Validate request types, lengths, identifiers and required fields. Use high-entropy API keys, support key rotation and apply rate limiting to protect your service. Do not log API keys, visitor passwords, complete request bodies or unredacted personal data.
Common Request Fields
Every example contains a config object:
Field | Proposed requirement | Purpose |
|---|---|---|
config.externalId | Required non-empty string | Identifies the customer or cabinet configuration in your service |
config.keyGroupIds | Non-empty array of non-empty strings for Keys; may be supplied on other operations | Selected cabinet key-group IDs |
The proposed public envelope keeps the existing lifecycle field names but does not require the cabinet API base URL or vendor-specific settings. Those belong in your service's configuration. Treat externalId and all key and user IDs as strings; preserve leading zeroes.
The exact set of forwarded configuration fields is a design decision for the generic interface, not a confirmed behaviour of a deployed API.
Endpoint Specifications
Verify
Validate the KMI API key and confirm that the selected customer configuration can reach the cabinet system. Do not create a cabinet user.
Request:
{
"config": {
"externalId": "site-123"
}
}Proposed response - 200:
{ "code": "SUCCESS" }Return an error if authentication or the required connection check fails. A successful Verify response does not prove that user creation, selected groups or permissions work.
Resources
Return the key groups available for the configured customer. Use the same request shape as Verify.
Proposed response - 200:
{
"keyGroups": [
{ "id": "group-01", "name": "Site keys" },
{ "id": "group-02", "name": "Vehicle keys" }
]
}The response contains a top-level keyGroups array, without a code wrapper. Each group needs a stable string id and a display name. If the cabinet does not have native groups, expose an agreed equivalent that lets the administrator select the intended inventory.
Keys
Return the complete set of keys in the selected groups.
Request:
{
"config": {
"externalId": "site-123",
"keyGroupIds": ["group-01", "group-02"]
}
}Proposed response - 200:
{
"code": "SUCCESS",
"data": [
{ "id": "001", "name": "Plant room" },
{ "id": "002", "name": "Store room" }
]
}Each key needs a stable string id and a display name. The ID can be the cabinet ID or a stable ID that your service maps to the cabinet. The proposed Sine behaviour uses it as the key's reference and sends it back in requestedKeyIds.
Retrieve every required cabinet API page and deduplicate keys that belong to multiple selected groups. A successful response represents a complete inventory snapshot. Sine may remove previously synchronised keys that are absent from it, so return an error if the inventory is incomplete or cannot be retrieved. Return an empty data array only when the complete selected inventory is genuinely empty.
The existing Sine asset synchronisation limits the generated identifier to 100 UTF-16 code units, including the asset-type prefix, the :: separator and the key ID. An ID that exceeds the remaining space is skipped rather than truncated. Confirm this limit and the site's prefix for the generic framework. If a cabinet ID does not fit, maintain a stable, collision-safe mapping in your service rather than shortening it blindly.
Provision
Create or prepare a cabinet user for a visitor, without granting access to the requested keys until Activate.
Request:
{
"config": {
"externalId": "site-123",
"keyGroupIds": ["group-01"]
},
"visitId": "1e851635-4b94-4a97-9b68-15be6bda1c3f",
"visitor": {
"firstName": "Example",
"lastName": "Visitor",
"email": "[email protected]",
"mobile": "+61400000000"
},
"validFrom": "2026-11-03T00:00:00.000Z",
"validTo": "2026-11-03T08:00:00.000Z",
"timezone": "Australia/Adelaide",
"requestedKeyIds": ["001", "002"]
}Field | Proposed requirement | Meaning |
|---|---|---|
visitId | Required non-empty string | Sine pass ID; use it with externalId to correlate the visit |
visitor.firstName, visitor.lastName | Required non-empty strings | Visitor name |
visitor.email, visitor.mobile | Optional strings; at least one must be non-empty | Available contact details |
validFrom, validTo | Required strings | Visit validity timestamps in ISO UTC format |
timezone | Required non-empty string | Site timezone for cabinet-specific date handling |
requestedKeyIds | Required non-empty array of non-empty strings | Selected key IDs returned by your service |
Proposed response - 201:
{
"code": "PROVISIONED",
"data": {
"keyUserId": "042381",
"password": "<REPLACE_ME>"
}
}Return non-empty string values for keyUserId and password. The user ID above is synthetic; replace the password placeholder only with a test credential in a secure environment. Sine uses these values in later lifecycle processing and may email them to the visitor after activation.
Store the association between externalId, visitId and keyUserId so that you can reconcile repeated or uncertain requests. Later operations include externalId and keyUserId, but not visitId; use the stored association when you need the visit context. Do not grant the requested key permissions at this stage.
Activate
Enable the provisioned user for the requested keys after the visitor is checked in or approved.
Request:
{
"config": {
"externalId": "site-123",
"keyGroupIds": ["group-01"]
},
"keyUserId": "042381",
"requestedKeyIds": ["001", "002"]
}Proposed response - 200:
{ "code": "ACTIVATED" }Require a non-empty keyUserId string and a non-empty requestedKeyIds array of non-empty strings. Validate that the user and keys belong to the customer, then apply the appropriate cabinet permissions. Handle repeat activation safely. In the proposed lifecycle, Sine attempts credential email after successful activation when the visitor has an email address.
Check-return
Report which requested keys remain outstanding for the provisioned user. The request uses the same fields as Activate.
Proposed response - 200:
{
"code": "SUCCESS",
"unreturnedKeyIds": ["002"]
}Require a non-empty keyUserId string and a requestedKeyIds array of non-empty strings; the array may be empty. unreturnedKeyIds is a top-level array containing only IDs from the request. Return an empty array only when your service can determine that none of the requested keys are outstanding under the agreed custody rules.
Define how your cabinet reports returned keys, missing keys and keys held by another user. If custody data is unavailable or ambiguous, return an error rather than reporting a confirmed return. Check-return does not itself update a Sine asset assignment.
Deprovision
Remove the visit's cabinet access. The request contains the same config, keyUserId and requestedKeyIds fields as Activate.
Proposed response - 200:
{ "code": "DEPROVISIONED" }Require a non-empty keyUserId string and a requestedKeyIds array of non-empty strings; the array may be empty. Remove or disable the visit's access according to your cabinet's capabilities, even when no key IDs are supplied. Handle an already-removed identity safely and do not remove another visitor's access. The proposed lifecycle calls Deprovision on pass rejection and after the return grace period on sign-out.
Errors and Recovery
Error Responses
Return a non-success HTTP status if your service cannot authenticate, validate or complete a request. A proposed error response is:
{
"code": "REQUEST_FAILED_ERROR",
"message": "Unable to complete the requested operation."
}Use an appropriate status such as 400 for invalid input, 401 for invalid credentials, 403 for forbidden access or 409 for a conflict. Use a server-error status for an unavailable cabinet or an unexpected downstream failure. The exact error code catalogue remains to be agreed.
Do not expose credentials, PINs, personal data or cabinet stack traces in error responses. In particular, do not report failed or incomplete inventory and custody checks as successful empty arrays.
Timeouts, Retries and Duplicate Requests
The generic framework's timeout and retry policy is not yet defined. Design your service for repeated requests and uncertain outcomes:
- Correlate Provision using externalId and visitId, and retain the mapping to keyUserId for later operations.
- Make Activate and Deprovision safe to repeat.
- On a repeated or timed-out Provision, reconcile with the cabinet before creating another user.
- Record enough information to recover partial failures without logging secrets.
- Do not assume a timed-out request had no effect or that requests are delivered exactly once.