Vega Access API v0.1.0
Vega Access connects the access-control system of a property (doors, readers, keypads) to partner platforms such as guest-messaging, property-management and access-orchestration services. Every door is exposed as a lock with live state, remote lock/unlock, and scheduled guest PIN codes.
- Base URL:
https://api.vhostico.com(also served under/v1) - Authorization server:
https://connect.vhostico.com/oauth/authorize, token endpointhttps://api.vhostico.com/oauth/token - JSON over HTTPS; timestamps are ISO-8601 UTC (
2026-11-02T20:00:00Z); time zones are IANA names. - Commands are asynchronous: the API answers immediately with
status: "pending", the final result arrives as a webhook event.
Partner credentials (client_id, client_secret, redirect URIs) are issued by Vega Access on request.
Authentication (OAuth 2.0 authorization code)
- Redirect the property operator to the authorization URL.
prompt=loginalways forces a fresh sign-in;stateis returned unchanged. PKCE (code_challenge, S256) is supported and optional. - The operator signs in and approves the requested scopes.
- We redirect to your registered
redirect_uriwithcodeandstate. Several redirect URIs can be registered per client (production, staging, localhost). - Exchange the code at the token endpoint (client authentication via HTTP Basic or form fields).
GET https://connect.vhostico.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fpartner.example.com%2Foauth%2Fcallback
&scope=locks.read%20locks.write&prompt=login&state=xyz123
POST https://api.vhostico.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=CODE&redirect_uri=https://partner.example.com/oauth/callback
&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
{"access_token": "vat_...", "token_type": "Bearer", "expires_in": 3600,
"refresh_token": "vrt_...", "scope": "locks.read locks.write", "user_id": "acct_4f0d2a9c61b7e835"}
user_id is a permanent account identifier: it never changes and does not depend on e-mail addresses.
Access tokens live one hour. Refresh with grant_type=refresh_token: you get a new access token and the same
refresh token; previously issued access tokens stay valid until they expire, so parallel workflows are never interrupted.
Revoke a grant with POST /oauth/revoke (token=...).
Scopes:
locks.read- See your doors and whether they are online, locked or openlocks.write- Lock and unlock your doors and manage guest PIN codes
Call the API with Authorization: Bearer <access_token>.
API reference
OAuth2
/oauth/authorizeStart authorization (login + consent page)
Host: https://connect.vhostico.com
/oauth/tokenExchange an authorization code or refresh token
Host: https://api.vhostico.com
/oauth/revokeRevoke a refresh or access token (RFC 7009)
Host: https://api.vhostico.com
Locks
/locksList all locks the account can access
Response
{"locks": [{"device_id": "lck_9f2c41d0a7b3e655", "lock_id": "lck_9f2c41d0a7b3e655", "name": "Room 101",
"device_type": "smart_lock", "device_model": "Vega Access Door Controller", "online": true,
"battery_level": null, "power_source": "wired", "timezone": "America/Toronto",
"device_time": "2026-10-30T06:00:00Z", "lock_state": "locked", "door_state": "closed",
"bridge_id": "site_2b8e0c71d9a4f312", "site_name": "Vega Demo Hotel", "address": "..."}]}
Host: https://api.vhostico.com
/locks/{lock_id}Get one lock
Path: lock_id
Host: https://api.vhostico.com
/locks/{lock_id}/actionsLock or unlock; result follows as lock.action_completed
Path: lock_id
| Field | Type | Required | Notes |
|---|---|---|---|
action | string one of lock, unlock | yes | |
custom_id | string |
Request
{"action": "unlock", "custom_id": "req_98aa221"}
Response
{"action_id": "act_23d1c0a95e7f4b18", "custom_id": "req_98aa221", "status": "pending",
"lock_id": "lck_9f2c41d0a7b3e655", "action": "unlock"}
Host: https://api.vhostico.com
/actions/{action_id}Get the status of a lock action
Path: action_id
Host: https://api.vhostico.com
Access codes
/locks/{lock_id}/access_codesList codes on a lock
Path: lock_id
Host: https://api.vhostico.com
/locks/{lock_id}/access_codesCreate a PIN code; result follows as access_code.created
Path: lock_id
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | |
code | string | yes | 4-8 digits, unique per property |
starts_at | string (date-time) | ||
ends_at | string (date-time) |
Request
{"name": "Jane Lo", "code": "4829",
"starts_at": "2026-11-02T20:00:00Z", "ends_at": "2026-11-05T16:00:00Z"}
Response
{"access_code_id": "ac_55301c2e9b0d7a41", "lock_id": "lck_9f2c41d0a7b3e655", "name": "Jane Lo",
"code": "4829", "starts_at": "2026-11-02T20:00:00Z", "ends_at": "2026-11-05T16:00:00Z", "status": "pending"}
Host: https://api.vhostico.com
/access_codes/{access_code_id}Get one code
Path: access_code_id
Host: https://api.vhostico.com
/access_codes/{access_code_id}Change PIN, name or schedule; result follows as access_code.updated
Path: access_code_id
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | ||
code | string | ||
starts_at | string (date-time) | ||
ends_at | string (date-time) |
Request
{"ends_at": "2026-11-06T16:00:00Z"}
Response
{"access_code_id": "ac_55301c2e9b0d7a41", "status": "pending", ...}
Host: https://api.vhostico.com
/access_codes/{access_code_id}Remove a code; result follows as access_code.deleted
Path: access_code_id
Response
{"access_code_id": "ac_55301c2e9b0d7a41", "status": "pending"}
Host: https://api.vhostico.com
Webhooks
/webhooksShow the registered webhook
Host: https://api.vhostico.com
/webhooksRegister (or replace) the single webhook endpoint for this environment
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | yes | |
events | array | Event types to receive; empty = all |
Request
{"url": "https://partner.example.com/events",
"events": ["lock.action_completed", "access_code.created", "access_code.used"]}
Response
{"webhook_id": "wh_11828a0c3f5e9d27", "url": "https://partner.example.com/events", "status": "active",
"events": [...], "signing_secret": "whsec_..."}
Host: https://api.vhostico.com
/webhooks/{webhook_id}Disable the webhook
Path: webhook_id
Host: https://api.vhostico.com
Service
/healthLiveness check
Host: https://api.vhostico.com
Webhooks & events
Register one webhook URL per environment with POST /webhooks, authenticated with your client credentials
(HTTP Basic client_id:client_secret). Registering again replaces the URL and rotates the signing secret.
Events for every account that has authorized your client are delivered to that URL; use user_id to route them.
- Delivery: HTTPS POST, JSON body. Any 2xx response acknowledges the event.
- Retries: exponential backoff (10 s doubling, capped at 1 h) for 48 hours with an unchanged payload. Events are delivered in order: a later event waits until the earlier one is acknowledged.
- Headers:
X-Vega-Event-Id,X-Vega-Timestamp,X-Vega-Signature: v1=HMAC_SHA256(signing_secret, timestamp + "." + body)(hex).
{"event_id": "evt_7c1e5a0b93d24f68", "event_type": "lock.action_completed", "user_id": "acct_4f0d2a9c61b7e835",
"device_id": "lck_9f2c41d0a7b3e655", "lock_id": "lck_9f2c41d0a7b3e655", "timestamp": "2026-10-30T06:00:02Z",
"action_id": "act_23d1c0a95e7f4b18", "custom_id": "req_98aa221", "action": "unlock",
"status": "success", "performed_via": "remote_api",
"data": {"action_id": "...", "custom_id": "...", "action": "unlock", "status": "success", "performed_via": "remote_api"}}
Event types: device.added, device.removed, device.online, device.offline, bridge.online, bridge.offline, device.time_wrong, device.low_battery, lock.locked, lock.unlocked, lock.action_completed, door.opened, door.closed, access_code.created, access_code.updated, access_code.deleted, access_code.used, access_code.incorrect_attempt.
access_code.created / updated carry the full access_code object and
status success or failed (with error_code).
access_code.used includes access_code_id; access_code.incorrect_attempt includes the entered pin.
performed_via is one of pin_code, mobile_app, remote_api, fob_card, bluetooth_key, manual_turn.
Errors
Errors use HTTP status codes and a JSON body:
{"error_code": "DEVICE_OFFLINE", "message": "The device is currently offline and cannot accept commands."}
| error_code | HTTP | Meaning |
|---|---|---|
| UNAUTHORIZED | 401 | Missing, expired or revoked access token |
| INSUFFICIENT_SCOPE | 403 | Token lacks the required scope |
| NOT_FOUND | 404 | Unknown lock, access code or action (or not in your account) |
| INVALID_REQUEST | 400 | Malformed body or missing field |
| INVALID_PIN_FORMAT | 400 | PIN must be 4-8 digits |
| INVALID_SCHEDULE | 400 | ends_at before starts_at, or in the past |
| PIN_CONFLICT | 409 | The PIN is already used on this door or by another guest at the property. The same guest (same name and schedule) may use one PIN on several doors. |
| DEVICE_OFFLINE | 409 | The door or the property controller is offline |
| CODE_BEING_REMOVED | 409 | The code is being deleted |
| RATE_LIMITED | 429 | Too many attempts |
OAuth endpoints return standard OAuth errors ({"error": "invalid_grant", "error_description": "..."}).
PIN codes are unique per property. Updating a code that the same guest holds on several doors applies the change to all of those doors.