API AUTHENTICATION
GET app is public and does not require authentication.
The API uses Bearer authentication over HTTPS. Sign in with POST login, sending your username and password in the request body. Use the returned user access token in the Authorization header of authenticated requests.
Authorization: Bearer USER_TOKEN
Desktop, mobile and other clients use the same endpoints and authentication. Keep credentials and refresh tokens in the HTTPS request body, and send user access tokens in the Authorization header.
Where documented, the Authorization header also accepts Bearer API_KEY. Include the user token parameter when an endpoint requires a user identity. The rooms endpoint requires a user Bearer token. Public clients should use user access tokens without embedding an API key.
Missing, expired or revoked user authentication returns HTTP 401. Authenticated requests without the required permission return HTTP 403; resources outside the user's access scope may return HTTP 404. Endpoint-specific response codes are listed below.
GET APPLICATION
GET https://vt.pageier.com/api/app
Returns the application name and logo configured in the administration settings. This endpoint is public: no login, user token or API key is required. Use HTTPS.
HEADERS
No authentication headers are required.
QUERY PARAMETERS
None.
REQUEST EXAMPLE
GET /api/app HTTP/1.1
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
message | String | ok. |
data | Object | Public application information. |
data.name | String | Configured application name. |
data.logo_url | String or null | Absolute URL of the configured logo in backend/assets. Uses the small logo when the main logo is empty. Null when no logo is configured or its filename is invalid. A non-null URL does not guarantee that the image file is available. |
data.registration_enabled | Boolean | Whether self-registration is enabled in application settings (enable_registration). Use this value to show or hide the registration option. Registration requests remain subject to the registration endpoint authentication and license requirements. |
data.validate_email | Boolean | Whether email verification is required for account activation after registration, according to application settings. Independent of whether registration is enabled. |
RESPONSE EXAMPLE
{
"message": "ok",
"data": {
"name": "Virtual Tour Platform",
"logo_url": "https://example.com/backend/assets/logo_123.png",
"registration_enabled": true,
"validate_email": true
}
}
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTThis endpoint accepts no query parameters. Remove the supplied parameters and repeat the GET request. | |
| CODE 405 | METHOD_NOT_ALLOWEDThe HTTP method is unsupported. Use GET to retrieve application information; OPTIONS is available for browser preflight requests. | |
| CODE 426 | HTTPS_REQUIREDCall the HTTPS API URL. If TLS terminates at a proxy, an administrator must configure trusted proxy handling. | |
| CODE 500 | INTERNAL_ERRORThe server could not read the application information. Retry later and retain request_id for support if the error persists. | |
| CODE 503 | API_SETUP_REQUIREDThe application configuration is unavailable. An administrator must complete or repair the application setup. SERVICE_UNAVAILABLEThe application settings are unavailable. Retry later; if the error persists, ask an administrator to check the settings and database. | |
LOGIN
POST https://vt.pageier.com/api/login
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Content-Type | Yes | String | application/json or application/x-www-form-urlencoded; HTTPS is required. |
Authorization | No | String | No header is needed for sign-in, verification or refresh. Logout also accepts Bearer USER_TOKEN. The configured API key is optional. |
Idempotency-Key | Conditional | String | Required only for action=refresh. Use 8-128 characters from A-Z, a-z, 0-9 and ._:-. Repeat the same key and body after a timeout; different content returns 409 IDEMPOTENCY_CONFLICT. Refresh responses expire after 24 hours; an expired key returns 409 IDEMPOTENCY_KEY_EXPIRED. |
Sign in
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | No | String | login (default). |
username | Yes | String | Username or email address. |
password | Yes | String | Password, sent in the HTTPS request body. |
device_name | No | String | Session label, maximum 255 characters. |
include | No | Array | ["session"] includes the refresh token and expiration times. |
REQUEST EXAMPLE
{
"username": "[email protected]",
"password": "YOUR_PASSWORD",
"include": [
"session"
]
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
message | String | ok. |
token | String | User access token. Send it in Authorization: Bearer USER_TOKEN; validity: 24 hours. |
login_url | String | Direct web sign-in URL. |
demo | Boolean | Whether demo restrictions apply to this user for the current request. When true, tour and room changes, including panorama uploads and replacements, are blocked even for administrators. Determined by the configured demo user, server address and developer IP exception. Returned on successful login, MFA completion and session refresh. |
session | Object | Returned when include contains session, and on refresh. |
session.id / session.token_type | String | Session UUID and Bearer. |
session.expires_in | Integer | Access-token lifetime in seconds. |
session.expires_at / session.refresh_expires_at / session.session_expires_at | String | UTC ISO 8601 expiration times for access token, refresh token and absolute session lifetime. |
session.refresh_token | String | Rotating refresh token. Idle expiration: 30 days; absolute session lifetime: 90 days. |
RESPONSE EXAMPLE
{
"message": "ok",
"token": "USER_TOKEN",
"login_url": "https://example.com/backend/login.php?token=WEB_TOKEN",
"demo": false,
"session": {
"id": "3fcb2ef4-b582-45c4-8677-47dca660611d",
"token_type": "Bearer",
"expires_in": 86400,
"expires_at": "2026-10-23T12:00:00Z",
"refresh_token": "REFRESH_TOKEN",
"refresh_expires_at": "2026-11-21T12:00:00Z",
"session_expires_at": "2027-01-20T12:00:00Z"
}
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 202 | A sign-in verification code is required. No user token has been issued yet. Submit action=mfa with the returned challenge_id and a valid six-digit code. | |
code | String | MFA_REQUIRED when a verification code is required. |
challenge_id | String | One-use challenge identifier. |
expires_in | Integer | Challenge lifetime: 300 seconds. |
RESPONSE EXAMPLE
{
"code": "MFA_REQUIRED",
"challenge_id": "CHALLENGE_ID",
"expires_in": 300
}
Verify sign-in code
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | mfa. |
challenge_id | Yes | String | Challenge returned by sign-in. |
otp | Yes | String | Six-digit verification code. |
REQUEST EXAMPLE
{
"action": "mfa",
"challenge_id": "CHALLENGE_ID",
"otp": "123456"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
The same login response shown above. The original include selection is preserved.
Refresh session
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | refresh. |
refresh_token | Yes | String | Current refresh token, in the HTTPS body. |
REQUEST EXAMPLE
{
"action": "refresh",
"refresh_token": "REFRESH_TOKEN"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
The same login response, including session and a new refresh token. The previous access-token generation is invalidated. Replay the same Idempotency-Key and body after a timeout; the previous refresh response can be replayed for 24 hours.
Log out
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | logout. |
refresh_token | Without user Bearer | String | Refresh token of the session to revoke. Alternatively, omit this field and send Authorization: Bearer USER_TOKEN; a recognized expired access token can be used for logout. |
REQUEST EXAMPLE
{
"action": "logout",
"refresh_token": "REFRESH_TOKEN"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 204 | No content | |
Empty body. Repeating logout with the same refresh token also returns 204.
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 201 | {"message":"blocked"}The account is blocked or inactive. An authorized administrator must reactivate it before sign-in can succeed. |
|
| CODE 202 | {"message":"incorrect password"}The password does not match this account. Check it or reset the password, then sign in again. |
|
| CODE 203 | {"message":"incorrect username"}No account matches the supplied username or email address. Check the value before trying again. |
|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or invalid. Supply 8-128 characters using letters, digits or ._:-, and keep the same key when retrying the same operation. |
|
| CODE 401 | INVALID_API_KEYThe Authorization header is not accepted for this login action. Omit the header for sign-in or code verification, or provide the configured API key. SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. REFRESH_INVALIDThe refresh token is unknown, expired or belongs to a revoked session. Sign in again to obtain a valid session. REFRESH_REUSEDA refresh token that was already rotated was submitted with a different Idempotency-Key. The session has been revoked. Sign in again; retry a lost refresh response only with its original key and body. MFA_INVALIDThe verification code is incorrect, or the sign-in challenge has expired, was already used or reached its attempt limit. Check the code and start sign-in again if the challenge is no longer valid. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. |
|
| CODE 409 | SESSION_LIMITThe maximum number of active sessions for this user has been reached. Log out another session or wait for a session to expire before signing in again. REFRESH_RESULT_SUPERSEDEDA later refresh has already replaced the token set returned for this request. Use the most recently issued tokens, or sign in again if those tokens are unavailable. IDEMPOTENCY_CONFLICTThis Idempotency-Key was already used with different request data. Restore the original body for a retry; use a different key only for a separate operation. IDEMPOTENCY_KEY_EXPIREDThe stored response for this key has passed its replay deadline, so the key cannot be reused. Check the resource status before starting any separate operation with a different key. REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. |
|
REGISTER
Allows to register a user from an external site.
POST https://vt.pageier.com/api/register
AUTHORIZATION
All - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY.
BODY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
username |
✓ | String | The user's username |
email |
✓ | String | The user's e-mail address |
password |
✓ | String | The user's password |
id_plan |
Integer | The id of the plan to associate to the user |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | User registered successfully | |
id_user |
Integer | The id of the added user |
validate_mail |
Boolean | If the user need to validate it's account |
| CODE 201 | The supplied username already belongs to another account. Choose a different username and submit the request again. | |
| CODE 202 | The supplied email address already belongs to another account. Use a different address, or sign in to the account associated with that address. | |
GET USERS
Retrieve the list of all the users.
GET https://vt.pageier.com/api/users
AUTHORIZATION
Administrator - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY together with the user token parameter.
QUERY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
token |
When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
offset |
Integer | The offset of the first item returned | |
limit |
Integer | The maximum number of entries to return |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
data |
Array | Contains the list of all the users |
| CODE 404 | No users match this page of results. Check limit and offset, or request the first page. | |
GET USER
Retrieve a user's details.
GET https://vt.pageier.com/api/user
AUTHORIZATION
Administrator - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY together with the user token parameter.
QUERY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
token |
When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
id |
✓ | Integer | The id of the user |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
data |
Array | Contains the user information |
| CODE 404 | No user matches the supplied id. Verify the identifier before repeating the request. | |
GET PROFILE
GET https://vt.pageier.com/api/user?action=me
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. Bearer API_KEY together with the user token parameter is also accepted. |
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | me. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
id / username / email | String | Authenticated user identifier, username and email. |
role | String | administrator, customer, team_member or editor. |
demo | Boolean | Whether demo restrictions apply to the authenticated user for this request, using the configured demo user, server address and developer IP exception. When true, tour and room changes, including panorama uploads and replacements, are blocked even for administrators. Read capabilities.actions for effective permissions; false does not by itself grant write access. |
first_name / last_name / language | String | Profile fields. |
account_id | String | Account associated with the authenticated user. |
plan_status | String | Current plan status. |
required_actions | Array | Required account actions, for example complete_profile. |
tour_creation | Object | allowed, required_fields, default_active, default_language, template_id, templates[{id,name}], supports_arbitrary_templates. Only the configured template is accepted. |
capabilities.actions | Object | Action names mapped to {allowed: boolean, reasons: string[]}. Profile actions: create_tour. Tour actions: read_tour, edit_tour, delete_tour, upload_panorama, replace_panorama, create_room, edit_room, delete_room, publish_tour, create_room_link. |
capabilities.quotas.billing_account_id | String | Account charged for this resource, including actions by collaborators. |
capabilities.quotas.limits_known | Boolean | False when the account has no usable plan configuration. |
capabilities.quotas.storage_policy | String | Identifier of the storage accounting policy. |
capabilities.quotas.tours / tours_month / rooms / rooms_in_tour / storage_bytes | Object | Each dimension returns {limit, unlimited, used, reserved, remaining, over_limit}. Counts and storage bytes are integers. Unlimited: limit=null, unlimited=true, remaining=null. Unknown limits: limits_known=false, limit=null, unlimited=false, remaining=null. Zero is a real limit. |
capabilities.quotas.physical_reserved_bytes | Integer | Physical storage bytes reserved for processing. |
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe requested resource does not exist, is outside your access scope, belongs to another tour or user, or is no longer available. Check the identifiers and parent tour; this response does not reveal whether another user owns it. |
|
| CODE 409 | REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. |
|
{
"error": {
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "a1b2c3",
"retryable": false,
"details": {}
}
}
ADD USER
Adding a user by the administrator.
POST https://vt.pageier.com/api/user
AUTHORIZATION
Administrator - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY together with the user token parameter.
BODY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
token |
When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
username |
✓ | String | The user's username |
email |
✓ | String | The user's e-mail address |
password |
✓ | String | The user's password |
role |
✓ | Enum | The user's role chosen from super_admin, administrator, editor, customer |
id_plan |
Integer | The id of the plan to associate to the user | |
first_name |
String | The user's first name | |
last_name |
String | The user's last name | |
company |
String | The user's company | |
tax_id |
String | The user's tax id | |
street |
String | The user's address | |
city |
String | The user's city | |
postal_code |
String | The user's postal code | |
province |
String | The user's province | |
country |
String | The user's country | |
tel |
String | The user's telephone number |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | User added successfully | |
id_user |
Integer | The id of the added user |
| CODE 201 | The supplied username already belongs to another account. Choose a different username and submit the request again. | |
| CODE 202 | The supplied email address already belongs to another account. Use a different address, or sign in to the account associated with that address. | |
| CODE 406 | The authenticated administrator cannot create a super_admin user. Use an account with super_admin privileges or choose a permitted role. | |
| CODE 407 | The role value is not supported. Use super_admin, administrator, editor or customer. Assigning super_admin also requires the corresponding account privileges. | |
EDIT USER
Editing a user by the administrator.
PUT https://vt.pageier.com/api/user
AUTHORIZATION
Administrator - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY together with the user token parameter.
Content-Type: application/x-www-form-urlencoded.
FORM BODY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
token |
When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
id_user |
✓ | Integer | The id of the user |
username |
String | The user's username | |
email |
String | The user's e-mail address | |
password |
String | The user's password | |
role |
Enum | The user's role chosen from super_admin, administrator, editor, customer |
|
id_plan |
Integer | The id of the plan to associate to the user | |
active |
Boolean | The status of the user account | |
first_name |
String | The user's first name | |
last_name |
String | The user's last name | |
company |
String | The user's company | |
tax_id |
String | The user's tax id | |
street |
String | The user's address | |
city |
String | The user's city | |
postal_code |
String | The user's postal code | |
province |
String | The user's province | |
country |
String | The user's country | |
tel |
String | The user's telephone number |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | User updated successfully | |
| CODE 201 | The supplied username already belongs to another account. Choose a different username and submit the request again. | |
| CODE 202 | The supplied email address already belongs to another account. Use a different address, or sign in to the account associated with that address. | |
| CODE 404 | No user matches the supplied id. Read the user list and select an existing user before updating it. | |
| CODE 406 | The authenticated administrator cannot edit a super_admin user or assign the super_admin role. Use an account with the required super_admin privileges. | |
| CODE 407 | The role value is not supported. Use super_admin, administrator, editor or customer. Assigning super_admin also requires the corresponding account privileges. | |
GET PLANS
Get all the available subscription plans.
GET https://vt.pageier.com/api/plans
AUTHORIZATION
All - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY.
QUERY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
| No parameters. | |||
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
data |
Array | Contains the list of all the plans |
| CODE 404 | The plan catalog contains no entries. An administrator must configure an available plan before this list can return results. | |
GET SERVICES
Get all the available services to purchase.
GET https://vt.pageier.com/api/services
AUTHORIZATION
All - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY.
QUERY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
| No parameters. | |||
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
data |
Array | Contains the list of all the services |
| CODE 404 | The service catalog contains no entries. An administrator must configure an available service before this list can return results. | |
GET CREDITS
Get all the available credits to purchase.
GET https://vt.pageier.com/api/credits
AUTHORIZATION
All - SaaS version
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY.
QUERY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
| No parameters. | |||
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
data |
Array | Contains the list of all the credits |
| CODE 404 | The credit-package catalog contains no entries. An administrator must configure a credit package before this list can return results. | |
GET TOURS
GET https://vt.pageier.com/api/tours
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. Bearer API_KEY together with the user token parameter is also accepted. |
List tours
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | No | String | list (default). |
token | When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
limit | No | Integer | Maximum entries; default 99999; positive integer. |
offset | No | Integer | First entry offset; default 0. |
q | No | String | Search tour name or author; maximum 200 characters. |
sort | No | String | created_desc (default), created_asc or name_asc; ID breaks ties. |
can_create_rooms | No | Boolean | true returns only tours currently allowing room creation. |
include | No | String or array | Requested projections: capabilities, upload_policy, viewer, pagination. Query example: include=capabilities,pagination. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
message | String | ok. |
data | Array | Accessible tours. Each item: id, external, name, date_created, author, id_user, active, count_rooms, background_image. Optional projections match include. |
page | Object | Returned only when include contains pagination. |
page.limit / page.offset | Integer | Applied limit and starting offset. |
page.has_more | Boolean | Whether another page is available. |
page.next_offset | Integer or null | Offset for the next request; null on the last page. |
RESPONSE EXAMPLE
{
"message": "ok",
"data": [
{
"id": "41",
"external": 0,
"name": "Property tour",
"date_created": "2026-10-22 12:00:00",
"author": "Studio",
"id_user": "7",
"active": 1,
"count_rooms": 2,
"background_image": ""
}
],
"page": {
"limit": 25,
"offset": 0,
"has_more": false,
"next_offset": null
}
}
Tour detail
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | detail. |
tour_id | Yes | String or integer | Accessible tour identifier. |
token | When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
id / owner_id | String | Tour identifier and owner identifier. |
name / author / language | String | Tour name, author and language. |
active | Boolean | Whether the tour is active. |
room_count / ready_room_count | Integer | Total rooms and visible rooms. |
initial_room_id | String or null | Initial room identifier. |
etag | String | Quoted version also returned in the ETag response header. |
capabilities.actions | Object | Action names mapped to {allowed: boolean, reasons: string[]}. Profile actions: create_tour. Tour actions: read_tour, edit_tour, delete_tour, upload_panorama, replace_panorama, create_room, edit_room, delete_room, publish_tour, create_room_link. |
capabilities.quotas.billing_account_id | String | Account charged for this resource, including actions by collaborators. |
capabilities.quotas.limits_known | Boolean | False when the account has no usable plan configuration. |
capabilities.quotas.storage_policy | String | Identifier of the storage accounting policy. |
capabilities.quotas.tours / tours_month / rooms / rooms_in_tour / storage_bytes | Object | Each dimension returns {limit, unlimited, used, reserved, remaining, over_limit}. Counts and storage bytes are integers. Unlimited: limit=null, unlimited=true, remaining=null. Unknown limits: limits_known=false, limit=null, unlimited=false, remaining=null. Zero is a real limit. |
capabilities.quotas.physical_reserved_bytes | Integer | Physical storage bytes reserved for processing. |
upload_policy | Object | media_types, projection, aspect_ratio, max_size_bytes, max_width, max_height, max_pixels, part_size_bytes, upload_ttl_seconds, checksum. JPEG/PNG, full equirectangular 2:1, SHA-256. Use the effective limits returned for the tour. |
viewer | Object | url, active, availability (available, empty, inactive), room_visibility_policy (included_when_ready), protections_preserved. The viewer URL retains the tour access protections. |
Returns the tour and ETag response header. The same fields are returned by tour creation and update.
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe requested resource does not exist, is outside your access scope, belongs to another tour or user, or is no longer available. Check the identifiers and parent tour; this response does not reveal whether another user owns it. {"message":"no tours found"}No accessible tours match the list request. Check your filters and account access; changing pagination may also return results. |
|
| CODE 409 | REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. |
|
{
"error": {
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "a1b2c3",
"retryable": false,
"details": {}
}
}
ADD / UPDATE TOUR
POST https://vt.pageier.com/api/tours
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. Bearer API_KEY together with the user token parameter is also accepted. |
Content-Type | Yes | String | application/json. |
Idempotency-Key | Conditional | String | Required only for action=create_tour. Use 8-128 characters from A-Z, a-z, 0-9 and ._:-. Repeat the same key and body after a timeout; different content returns 409 IDEMPOTENCY_CONFLICT. Responses expire after 7 days; an expired key returns 409 IDEMPOTENCY_KEY_EXPIRED. |
If-Match | For update_tour | String | The exact quoted ETag returned for the resource. Missing: 428 PRECONDITION_REQUIRED; stale: 412 VERSION_CONFLICT. |
Create tour
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | create_tour. |
name | Yes | String | Tour name, maximum 255 characters. |
author | No | String | Author, maximum 255 characters. |
language | No | String | Supported locale; defaults to the configured language. |
template_id | No | String or integer | Only the configured template returned by GET user?action=me. |
token | When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
REQUEST EXAMPLE
{
"action": "create_tour",
"name": "Property tour",
"author": "Studio",
"language": "en_US"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 201 | Created successfully | |
Tour object with the fields documented under GET tours / Tour detail. Owner is derived from the authenticated user.
Update tour
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | update_tour. |
tour_id | Yes | String or integer | Accessible tour identifier. |
name | At least one of name / active | String | New tour name; requires edit_tour. |
active | At least one of name / active | Boolean | Activate or deactivate the tour; requires publish_tour. |
token | When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
REQUEST EXAMPLE
{
"action": "update_tour",
"tour_id": "41",
"name": "Updated property tour",
"active": true
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
Updated tour object and its ETag response header; fields match GET tours / Tour detail.
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or invalid. Supply 8-128 characters using letters, digits or ._:-, and keep the same key when retrying the same operation. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. PLAN_EXPIREDThe account paying for this operation has an expired subscription, which blocks the requested creation. Renew the subscription before trying again. PAYMENT_REQUIREDThe subscription has an invalid payment status. Resolve the billing issue before creating additional resources. PROFILE_INCOMPLETEThe user profile is missing required account information. Complete the profile, then repeat the operation. TOUR_LOCKEDThis tour is locked for the authenticated account. Ask an authorized administrator to unlock it before editing or adding content. PLAN_CONFIGURATION_REQUIREDThe account has no usable plan configuration. An administrator must assign or repair the plan before additional resources can be created. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe requested resource does not exist, is outside your access scope, belongs to another tour or user, or is no longer available. Check the identifiers and parent tour; this response does not reveal whether another user owns it. |
|
| CODE 409 | REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. TOUR_QUOTA_EXCEEDEDThe account has reached its total tour limit or monthly tour-creation limit. Free capacity or increase the applicable limit; for a monthly limit, you can also wait for the next period. IDEMPOTENCY_CONFLICTThis Idempotency-Key was already used with different request data. Restore the original body for a retry; use a different key only for a separate operation. IDEMPOTENCY_KEY_EXPIREDThe stored response for this key has passed its replay deadline, so the key cannot be reused. Check the resource status before starting any separate operation with a different key. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. |
|
| CODE 412 | VERSION_CONFLICTThe If-Match value does not match the resource's current ETag. Read the resource again, reconcile your changes and repeat the update with the returned ETag. |
|
| CODE 428 | PRECONDITION_REQUIREDThe required If-Match header is missing. Read the room or tour first and send its exact quoted ETag with the update or deletion request. |
|
| CODE 422 | INVALID_TEMPLATEThe supplied template_id is not the template configured for this account. Use the template returned by GET user?action=me, or omit template_id to use the configured default. |
|
{
"error": {
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "a1b2c3",
"retryable": false,
"details": {}
}
}
DELETE TOUR
DELETE https://vt.pageier.com/api/tours
Permanently deletes the tour, its rooms, navigation links and related records. Removes files that are no longer referenced by another tour or shared library, cancels pending uploads and releases their reservations. Requires capabilities.actions.delete_tour.allowed; permission to delete rooms alone does not grant permission to delete a tour. Tour, room and storage usage are recalculated for the tour owner account. An expired plan does not by itself prevent deletion.
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. |
If-Match | Yes | String | The exact quoted tour ETag returned by GET tours with action=detail. Missing: 428 PRECONDITION_REQUIRED; stale: 412 VERSION_CONFLICT. |
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
tour_id | Yes | String or integer | Tour identifier. No request body or action parameter. |
REQUEST EXAMPLE
DELETE /api/tours?tour_id=41 HTTP/1.1
Authorization: Bearer USER_TOKEN
If-Match: "TOUR_ETAG_FROM_DETAIL"
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 204 | No content | |
Empty body after deletion and file cleanup. If the connection times out, repeat the same request as the same user with the original If-Match: a completed deletion returns 204 again without duplicate effects. Idempotency-Key is not required. Active panorama processing returns 409 without deleting the tour. If file cleanup fails after deletion, the response is 503 with error.details.tour_deleted and error.details.cleanup_pending set to true; retry the same DELETE request to complete cleanup. Subsequent API calls also attempt pending cleanup. A deletion takes effect in the viewer as soon as the tour is removed; there is no undo.
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe tour does not exist or is outside your access scope. A retry for a deleted tour also requires the same authenticated user and original If-Match. Check the identifier and authentication; this response does not reveal another user's resources. |
|
| CODE 409 | REQUEST_IN_PROGRESSAn upload or panorama operation is running, or another request holds a required lock. The deletion has not started. Wait for Retry-After, then retry the same DELETE request with the same If-Match header. |
|
| CODE 412 | VERSION_CONFLICTThe If-Match value does not match the current tour ETag. Read the tour again and confirm that this is still the tour you intend to delete before retrying with its current ETag. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 428 | PRECONDITION_REQUIREDThe If-Match header is missing. Call GET tours?action=detail&tour_id=41 and send the exact quoted tour ETag in the deletion request. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required service is unavailable. If error.details.tour_deleted is true, the tour has already been deleted and file cleanup is pending: retry the same DELETE request with the original If-Match. Storage remains reserved until cleanup succeeds. Otherwise retry later; retain request_id for support. |
|
ERROR RESPONSE EXAMPLE
{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "The tour was deleted but file cleanup is pending; retry the same DELETE request",
"request_id": "a1b2c3",
"retryable": true,
"details": {
"tour_deleted": true,
"cleanup_pending": true
}
}
}
GET ROOMS
GET https://vt.pageier.com/api/rooms
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. |
List rooms
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Accessible tour identifier. |
action | No | String | list (default when id_room is absent). |
limit | No | Integer | Default 25; minimum 1, maximum 1000. |
offset | No | Integer | Default 0. |
q | No | String | Search room name; maximum 200 characters. |
sort | No | String | priority_asc (default) or name_asc. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
data | Array | Rooms with the fields shown in Room detail. An empty list returns 200. |
page.limit / page.offset | Integer | Applied limit and starting offset. |
page.has_more | Boolean | Whether another page is available. |
page.next_offset | Integer or null | Offset for the next request; null on the last page. |
RESPONSE EXAMPLE
{
"data": [
{
"id": "502",
"tour_id": "41",
"name": "Living room",
"type": "image",
"state": "ready",
"visible": true,
"priority": 0,
"yaw": 35,
"pitch": 0,
"northOffset": 90,
"job_id": "a68027ab-f7b3-46d3-9b6b-d78c8ef476a7",
"preview_url": "/api/rooms?id_tour=41&action=preview&id_room=502",
"panorama_url": "/api/rooms?id_tour=41&action=panorama&id_room=502",
"etag": "\"ROOM_VERSION\""
}
],
"page": {
"limit": 25,
"offset": 0,
"has_more": false,
"next_offset": null
}
}
Room detail
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour identifier. |
id_room | Yes | String or integer | Room in the specified tour. |
action | No | String | detail (default when id_room is supplied). |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
id / tour_id | String | Room identifier and parent tour identifier. |
name / type | String | Room name and media type. |
state | String | processing, ready, failed or deleting. |
visible | Boolean | True when the room is included in the viewer, subject to the tour access restrictions. |
priority | Integer | Room order. |
yaw | Integer | Saved horizontal direction of the initial view, in degrees. |
pitch | Integer | Saved vertical direction of the initial view, in degrees. |
northOffset | Integer | Saved compass north offset, in degrees. |
job_id | String or null | Current operation identifier. |
preview_url | String or null | Authenticated JPEG preview URL; null until ready. |
panorama_url | String or null | Authenticated URL for the room image stored in viewer/panoramas/ (not the original upload or multiresolution tiles). Send Authorization: Bearer USER_TOKEN. Null unless the image room is ready; a missing or invalid file returns 409 PANORAMA_NOT_READY. |
etag | String | Quoted room version; also returned in the ETag header for detail/update. |
RESPONSE EXAMPLE
{
"id": "502",
"tour_id": "41",
"name": "Living room",
"type": "image",
"state": "ready",
"visible": true,
"priority": 0,
"yaw": 35,
"pitch": 0,
"northOffset": 90,
"job_id": "a68027ab-f7b3-46d3-9b6b-d78c8ef476a7",
"preview_url": "/api/rooms?id_tour=41&action=preview&id_room=502",
"panorama_url": "/api/rooms?id_tour=41&action=panorama&id_room=502",
"etag": "\"ROOM_VERSION\""
}
Upload status
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour identifier. |
action | Yes | String | upload_status. |
upload_id | Yes | String | Upload UUID created by the authenticated user for this tour. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
id / tour_id | String | Upload identifier and parent tour identifier. |
state | String | initiated, uploading, validating, ready, consumed, cancelling, cancelled, expired, failed, deleting or deleted. |
target_room_id | String or null | Replacement target room; null for a new-room upload. Set by id_room at upload initiation. |
size_bytes / received_bytes | Integer | Declared total file size and bytes already received. |
part_size_bytes / part_count | Integer | Required part size and number of parts; the final part may be shorter. |
parts | Array | Received parts: {part_number: integer, size_bytes: integer, sha256: string}. |
expires_at | String | Fixed upload expiration time in UTC ISO 8601; 24 hours after initiation. |
reserved | Object | rooms, storage_bytes and physical_bytes reserved by this upload. |
job_id / room_id | String or null | Current operation and associated room identifiers. room_id is null after the room association has been released by deletion or cleanup; job status retains the operation target identifier. |
cleanup_pending | Boolean | True while file removal and storage release are still pending. |
error | Object or null | Failure details: http_status, code, message, retryable and optional details containing additional diagnostics. |
RESPONSE EXAMPLE
{
"id": "3fcb2ef4-b582-45c4-8677-47dca660611d",
"tour_id": "41",
"state": "initiated",
"size_bytes": 18350080,
"received_bytes": 0,
"part_size_bytes": 2097152,
"part_count": 9,
"parts": [],
"expires_at": "2026-10-23T12:00:00Z",
"reserved": {
"rooms": 1,
"storage_bytes": 18350080,
"physical_bytes": 1464336384
},
"job_id": null,
"room_id": null,
"cleanup_pending": false,
"error": null,
"target_room_id": null
}
Operation status
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour identifier. |
action | Yes | String | job_status. |
job_id | Yes | String | Current operation UUID belonging to the user and tour. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
id / tour_id / upload_id | String | Current operation, tour and upload identifiers. |
room_id | String or null | Room identifier, when applicable. |
state | String | pending, running, succeeded or failed. |
phase | String | Current processing phase. |
progress | Integer | Progress from 0 to 100. |
attempts | Integer | Number of executions. |
updated_at | String | Last update in UTC ISO 8601. |
cleanup_pending | Boolean | Whether cleanup is still pending. |
error | Object or null | Failure details: http_status, code, message, retryable and optional details containing additional diagnostics. A failed operation is returned with HTTP 200 by this status request. |
Status reads do not start or resume processing. Subsequent API requests detect abandoned validation, room creation and panorama replacement jobs after the processing timeout plus a 30-second grace period (default: 30 minutes and 30 seconds). Recovery runs only when no processing request or panorama generator holds the upload locks. The job is marked failed with PROCESSING_INTERRUPTED. Cleanup removes temporary files while retaining an already created visible room and its main panorama; for a replacement, it restores the previous panorama. Reservations are released after cleanup succeeds, and retained files and rooms continue to count toward usage. The retained room state becomes ready and the job remains failed. Successful jobs are preserved. Recovery does not resume a failed operation; use a new upload to replace the panorama when needed.
Preview and panorama image
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour identifier. |
id_room | Yes | String or integer | Room in the specified tour. |
action | Yes | String | preview for the JPEG preview, or panorama for the image in viewer/panoramas/. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
Send Authorization: Bearer USER_TOKEN. With action=preview, Content-Type is image/jpeg and the body contains the preview image. With action=panorama, Content-Type is image/jpeg or image/png according to the actual file, and the body contains the image stored in viewer/panoramas/. Both require access to the parent tour and a room in that tour. Images are available when the room is ready. Panorama retrieval supports image rooms and local or S3 storage.
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe requested resource does not exist, is outside your access scope, belongs to another tour or user, or is no longer available. Check the identifiers and parent tour; this response does not reveal whether another user owns it. |
|
| CODE 409 | REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. PREVIEW_NOT_READYThe room is not ready or its valid preview image is not available. Check the room and operation status before requesting the preview again. PANORAMA_NOT_READYThe room is not ready or its JPEG or PNG panorama file is missing or invalid. Check the room and operation status before retrying. UNSUPPORTED_ROOM_TYPEPanorama retrieval is available only for image rooms. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. |
|
{
"error": {
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "a1b2c3",
"retryable": false,
"details": {}
}
}
ADD ROOM / UPLOAD
POST https://vt.pageier.com/api/rooms
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. |
Content-Type | Yes | String | application/json or multipart/form-data for action=upload_part. |
Idempotency-Key | Conditional | String | Required for initiate_upload, complete_upload, create and create_room_link. Use 8-128 characters from A-Z, a-z, 0-9 and ._:-. Repeat the same key and body after a timeout; different content returns 409 IDEMPOTENCY_CONFLICT. Responses expire after 7 days; an expired key returns 409 IDEMPOTENCY_KEY_EXPIRED. |
Start upload
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour. Creation requires upload_panorama and create_room; replacement requires replace_panorama, which uses the room-edit permission. |
id_room | No | String or integer | Existing image room to replace. When supplied, the upload is bound to this room and reserves zero room slots. Omit to upload for a new room. |
action | Yes | String | initiate_upload. |
filename | Yes | String | File name without a path. |
media_type | Yes | String | image/jpeg or image/png. |
size_bytes | Yes | Integer | Actual input length in bytes; must satisfy upload_policy.max_size_bytes. |
sha256 | Yes | String | SHA-256 of the complete file, 64 lowercase hexadecimal characters. |
width / height | Yes | Integer | Actual image dimensions; width must equal 2 * height and satisfy upload_policy. |
projection | Yes | String | equirectangular. |
REQUEST EXAMPLE
{
"action": "initiate_upload",
"id_tour": "41",
"filename": "living-room.jpg",
"media_type": "image/jpeg",
"size_bytes": 18350080,
"sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"width": 8192,
"height": 4096,
"projection": "equirectangular"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 201 | Created successfully | |
id / tour_id | String | Upload identifier and parent tour identifier. |
state | String | initiated, uploading, validating, ready, consumed, cancelling, cancelled, expired, failed, deleting or deleted. |
target_room_id | String or null | Replacement target room; null for a new-room upload. Set by id_room at upload initiation. |
size_bytes / received_bytes | Integer | Declared total file size and bytes already received. |
part_size_bytes / part_count | Integer | Required part size and number of parts; the final part may be shorter. |
parts | Array | Received parts: {part_number: integer, size_bytes: integer, sha256: string}. |
expires_at | String | Fixed upload expiration time in UTC ISO 8601; 24 hours after initiation. |
reserved | Object | rooms, storage_bytes and physical_bytes reserved by this upload. |
job_id / room_id | String or null | Current operation and associated room identifiers. room_id is null after the room association has been released by deletion or cleanup; job status retains the operation target identifier. |
cleanup_pending | Boolean | True while file removal and storage release are still pending. |
error | Object or null | Failure details: http_status, code, message, retryable and optional details containing additional diagnostics. |
Location points to upload_status. Reserves one room slot for creation or zero slots for replacement, and the reported storage budget on the tour owner account. Replacement needs enough storage for both panoramas until old-file cleanup is complete.
RESPONSE EXAMPLE
{
"id": "3fcb2ef4-b582-45c4-8677-47dca660611d",
"tour_id": "41",
"state": "initiated",
"size_bytes": 18350080,
"received_bytes": 0,
"part_size_bytes": 2097152,
"part_count": 9,
"parts": [],
"expires_at": "2026-10-23T12:00:00Z",
"reserved": {
"rooms": 1,
"storage_bytes": 18350080,
"physical_bytes": 1464336384
},
"job_id": null,
"room_id": null,
"cleanup_pending": false,
"error": null,
"target_room_id": null
}
Transfer part
MULTIPART FORM PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour identifier. |
action | Yes | String | upload_part. |
upload_id | Yes | String | Upload UUID. |
part_number | Yes | Integer | From 1 to part_count. |
sha256 | Yes | String | SHA-256 of this part, 64 lowercase hexadecimal characters. |
file | Yes | Binary | One file part. Use multipart/form-data and the part_size_bytes returned by initiation; the final part may be shorter. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
Updated upload object, including received parts and checksums; same fields as Start upload. Repeating an identical part is accepted. A different part for the same number returns 409 PART_CONFLICT.
Complete upload
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour identifier. |
action | Yes | String | complete_upload. |
upload_id | Yes | String | Upload UUID. |
sha256 | Yes | String | SHA-256 of the complete file. |
REQUEST EXAMPLE
{
"action": "complete_upload",
"id_tour": "41",
"upload_id": "3fcb2ef4-b582-45c4-8677-47dca660611d",
"sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
job_id | String | Current operation identifier. Only the latest operation for an upload remains available. |
resource_type | String | upload or room. |
resource_id | String | Upload or room identifier, according to resource_type. |
state | String | succeeded on a completed POST; status requests may return pending, running, succeeded or failed. |
poll_url | String | GET URL for operation status using the same user Bearer token. |
Returns after file validation. The upload state becomes ready. Location contains the operation-status URL.
RESPONSE EXAMPLE
{
"job_id": "a68027ab-f7b3-46d3-9b6b-d78c8ef476a7",
"resource_type": "upload",
"resource_id": "3fcb2ef4-b582-45c4-8677-47dca660611d",
"state": "succeeded",
"poll_url": "/api/rooms?id_tour=41&action=job_status&job_id=a68027ab-f7b3-46d3-9b6b-d78c8ef476a7"
}
Create room
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour identifier; requires create_room and upload_panorama. |
action | No | String | create (default). |
name | Yes | String | Room name, maximum 255 characters. |
upload_id | Yes | String | Ready, unconsumed upload belonging to this user and tour, initiated without id_room. An upload bound to a replacement room cannot create a new room. |
REQUEST EXAMPLE
{
"id_tour": "41",
"name": "Living room",
"upload_id": "3fcb2ef4-b582-45c4-8677-47dca660611d"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 201 | Created successfully | |
job_id | String | Current operation identifier. Only the latest operation for an upload remains available. |
resource_type | String | upload or room. |
resource_id | String | Upload or room identifier, according to resource_type. |
state | String | Background processing state: pending, running or succeeded. The room is already created and visible. Reading poll_url for the final processing outcome is optional. |
poll_url | String | GET URL for operation status using the same user Bearer token. |
HTTP 201 confirms that the room has been created with visible=true and its main panorama has been saved. The room is immediately included in the viewer, subject to the tour access restrictions. The response contains resource_id (the created room identifier) and job_id and returns after the background processor acknowledges startup, without waiting for compression, derived images or multiresolution. The client can consider creation complete when it receives the room identifier; monitoring the background job is optional. The room state can remain processing while derived images are generated. Errors after acceptance are available through job_status and upload_status. If background processing fails, the created room and main panorama are retained, temporary files and reservations are released after cleanup, and the room state becomes ready while the job reports failed. One reserved room slot becomes consumed and remains in use while the room exists. Repeat the same Idempotency-Key and body after a connection timeout to get the same room and job without starting duplicate processing. A failed background operation is not resumed; replace the panorama with a new upload if processing must be repeated.
RESPONSE EXAMPLE
{
"job_id": "a68027ab-f7b3-46d3-9b6b-d78c8ef476a7",
"resource_type": "room",
"resource_id": "502",
"state": "pending",
"poll_url": "/api/rooms?id_tour=41&action=job_status&job_id=a68027ab-f7b3-46d3-9b6b-d78c8ef476a7"
}
Cancel upload
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Original tour identifier. |
action | Yes | String | cancel_upload. |
upload_id | Yes | String | Upload UUID owned by this user. Cleanup is allowed after tour permissions are revoked; consumed uploads cannot be cancelled. |
REQUEST EXAMPLE
{
"action": "cancel_upload",
"id_tour": "41",
"upload_id": "3fcb2ef4-b582-45c4-8677-47dca660611d"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 204 | No content | |
Empty body after cleanup. Repeating cancellation of an already cleaned upload returns 204. Reservations are released after file cleanup.
Create navigation link
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour; requires create_room_link. |
action | Yes | String | create_room_link. |
source_room_id / target_room_id | Yes | String or integer | Two different ready rooms in this tour. |
yaw | Yes | Number | Horizontal coordinate from -180 to 180. |
pitch | Yes | Number | Vertical coordinate from -90 to 90. |
bidirectional | No | Boolean | Also create a reverse link; default false. |
reverse_yaw / reverse_pitch | When bidirectional=true | Number | Reverse coordinates using the same yaw/pitch ranges. |
REQUEST EXAMPLE
{
"action": "create_room_link",
"id_tour": "41",
"source_room_id": "502",
"target_room_id": "503",
"yaw": 25,
"pitch": 0,
"bidirectional": true,
"reverse_yaw": -150,
"reverse_pitch": 0
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 201 | Created successfully | |
id / tour_id | String | Created link and parent tour identifiers. |
source_room_id / target_room_id | String | Linked room identifiers. |
reverse_id | String or null | Reverse link identifier; null for a one-way link. |
RESPONSE EXAMPLE
{
"id": "81",
"tour_id": "41",
"source_room_id": "502",
"target_room_id": "503",
"reverse_id": "82"
}
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or invalid. Supply 8-128 characters using letters, digits or ._:-, and keep the same key when retrying the same operation. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. PLAN_EXPIREDThe account paying for this operation has an expired subscription, which blocks the requested creation. Renew the subscription before trying again. PAYMENT_REQUIREDThe subscription has an invalid payment status. Resolve the billing issue before creating additional resources. PROFILE_INCOMPLETEThe user profile is missing required account information. Complete the profile, then repeat the operation. TOUR_LOCKEDThis tour is locked for the authenticated account. Ask an authorized administrator to unlock it before editing or adding content. PLAN_CONFIGURATION_REQUIREDThe account has no usable plan configuration. An administrator must assign or repair the plan before additional resources can be created. UNSUPPORTED_TOUR_TYPEThis upload and room-creation operation is unavailable for external tours and AR simulator tours. Select a standard tour. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe requested resource does not exist, is outside your access scope, belongs to another tour or user, or is no longer available. Check the identifiers and parent tour; this response does not reveal whether another user owns it. |
|
| CODE 409 | REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. ROOM_QUOTA_EXCEEDEDThe account or tour cannot accept another reserved or created room. Pending uploads also reserve room slots. Release unused uploads, remove a room or increase the applicable limit. STORAGE_QUOTA_EXCEEDEDThe tour owner account does not have enough unreserved storage for this operation. Release unused uploads, remove stored content or increase the storage allowance. MARKER_QUOTA_EXCEEDEDThe requested navigation link exceeds the account or tour marker limit. A one-way link requires one marker and a bidirectional link requires two. Free marker capacity or increase the limit. PROCESSING_CAPACITY_UNAVAILABLEThe server cannot reserve enough physical space for the upload, temporary files and generated images. Retry after capacity becomes available or ask the administrator to free server storage. IDEMPOTENCY_CONFLICTThis Idempotency-Key was already used with different request data. Restore the original body for a retry; use a different key only for a separate operation. IDEMPOTENCY_KEY_EXPIREDThe stored response for this key has passed its replay deadline, so the key cannot be reused. Check the resource status before starting any separate operation with a different key. PART_CONFLICTDifferent bytes are already stored for this upload and part_number. Retry with the identical part, or start another upload if the source file has changed. UPLOAD_INCOMPLETEThe upload does not contain every declared part or the total received length is incomplete. No validation job has started. To abandon an interrupted attempt, call cancel_upload to remove temporary files and release reservations, then initiate a new upload with a new Idempotency-Key. A response timeout alone does not confirm failure: check upload/job status before starting another attempt. UPLOAD_STATE_CONFLICTThe requested transfer or validation step is not allowed in the upload's current state. Read upload_status and continue with the action appropriate to the reported state. UPLOAD_NOT_READYThe upload has not completed image validation. Complete the upload and check its status; create the room only after the upload reaches ready. UPLOAD_ALREADY_CONSUMEDThis upload has already been used to create a room and cannot be reused or cancelled. Retry the original creation with its original Idempotency-Key to retrieve that result, or use another upload for another room. ROOM_NOT_READYThe room is processing, failed or being deleted, so the requested action is unavailable. Read the room and operation status, then wait for ready or resolve the processing error. STORAGE_TARGET_CHANGEDThe tour's storage destination changed during processing, so the operation cannot finish against that destination. Ask the administrator to confirm the storage configuration and pending cleanup before restarting the upload. UNSUPPORTED_ROOM_TYPEReplacement uploads require an existing image room with a supported JPEG or PNG panorama filename. Video, HLS and Lottie rooms cannot be replaced by this operation. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. PROCESSOR_UNAVAILABLEThe server cannot start the panorama processor. PHP_PATH is optional: PHP is located using the server PATH, then PHP_BINARY with the PHP-FPM path converted to its CLI counterpart, then PHP_BINDIR/php or the php command. Candidates are verified by actually starting PHP CLI, including when open_basedir restricts filesystem checks. Startup notices do not invalidate the CLI check. If detection fails, error.details.processor_attempts lists the candidate source, exit_code, reason and detected sapi when available. Reasons include spawn_failed, startup_timeout, command_not_found, not_executable, process_failed, missing_php_probe_output and not_cli. error.details.processor_diagnostic_id identifies the matching entry in the server PHP error log, where attempted paths and startup output are recorded; those paths and output are not included in the API response. If PHP_PATH is configured, it must start PHP CLI. An administrator must check PHP availability, required process functions and panorama-processing configuration. Processor startup is checked before creating an upload. Room creation and panorama replacement also require the background PHP process to acknowledge startup within three seconds; this checks startup only and does not wait for image processing. Failure to launch or acknowledge startup fails the operation and triggers cleanup. A previously failed operation keeps its stored error: after correcting the server, initiate a new upload with a new Idempotency-Key; replaying the failed operation does not restart processing. PROCESSING_FAILEDThe panorama or its derived images could not be generated successfully. Read the operation error and cleanup_pending status. For unexpected processing exceptions, details.processing_diagnostic_id identifies a server PHP log entry containing the original error; internal messages and paths are not returned by the API. Resolve the cause and complete cleanup before starting another upload; retrying the failed operation returns its recorded error. PROCESSING_TIMEOUTAn image generator exceeded its time limit and was stopped. Check operation and cleanup status, then ask the administrator to review processing capacity or timeout settings before starting another upload. PROCESSING_INTERRUPTEDThe operation was interrupted before completion, exceeded the abandoned-job recovery deadline, or was interrupted repeatedly. The failed operation is not resumed. Check operation and cleanup status, then start another upload after the cause is resolved and cleanup completes. TEMPORARY_STORAGE_UNAVAILABLEThe server cannot create, read or write the private upload files. An administrator must check free space and directory permissions. Check upload status before retrying; a failed upload must be restarted. |
|
| CODE 410 | UPLOAD_EXPIREDThe upload has reached its fixed 24-hour expiration time and no longer accepts writes. Start another upload and transfer the file again. |
|
| CODE 413 | UPLOAD_TOO_LARGEThe file or request exceeds the effective upload limit or a server request-size limit. Check upload_policy, use a smaller image when necessary and keep each part within part_size_bytes. |
|
| CODE 415 | UNSUPPORTED_MEDIA_TYPEThe file is not a supported JPEG or PNG, or its actual content does not match the declared media_type. Export the panorama in a supported format and submit the correct media type. |
|
| CODE 422 | CHECKSUM_MISMATCHThe received byte count or SHA-256 does not match the declared part or complete file. Verify the file bytes, part boundaries and checksums. If validation has already failed, start another upload with the corrected data. INVALID_IMAGEThe image is truncated, damaged or cannot be fully decoded. Export an intact panorama and start another upload. INVALID_PROJECTIONThe panorama must be a complete, correctly oriented, mono equirectangular image with a 2:1 aspect ratio. Its orientation and GPano metadata must match the pixels; cropped or incompatible projection metadata is rejected. IMAGE_DIMENSIONS_EXCEEDEDThe actual image dimensions differ from the declared width and height or exceed the tour's maximum width, height or pixel count. Correct the declared dimensions or resize the image to the returned upload_policy limits. INVALID_ROOM_SETA navigation link requires two different ready rooms in the specified tour. Select distinct source_room_id and target_room_id values. |
|
{
"error": {
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "a1b2c3",
"retryable": false,
"details": {}
}
}
EDIT ROOMS
PUT https://vt.pageier.com/api/rooms
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. |
Content-Type | Yes | String | application/json. |
If-Match | Yes | String | The exact quoted ETag returned for the resource. Missing: 428 PRECONDITION_REQUIRED; stale: 412 VERSION_CONFLICT. |
Idempotency-Key | For replace_panorama | String | Required for panorama replacement. Use 8-128 characters from letters, digits or ._:-. Retry with the same key, body and original If-Match. Replay responses are retained for 7 days; expired keys cannot be reused. |
Replace room panorama
First POST rooms with action=initiate_upload and id_room set to the existing room. Transfer parts with action=upload_part and validate with action=complete_upload. Then call this PUT operation with the ready upload. Requires replace_panorama (edit_rooms for an editor), a user Bearer token, the room If-Match and an Idempotency-Key. No additional room slot is consumed. The upload cannot be transferred to a different room or used to create a room.
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
action | Yes | String | replace_panorama. |
id_tour | Yes | String or integer | Parent tour identifier. |
id_room | Yes | String or integer | Image room bound to the upload at initiation. Must not be processing, being deleted or awaiting previous replacement cleanup. |
upload_id | Yes | String | Ready, unused replacement upload belonging to the authenticated user, tour and room. |
REQUEST EXAMPLE
PUT /api/rooms HTTP/1.1
Authorization: Bearer USER_TOKEN
Content-Type: application/json
If-Match: "ROOM_VERSION_FROM_DETAIL"
Idempotency-Key: replace-room-502-001
{
"action": "replace_panorama",
"id_tour": "41",
"id_room": "502",
"upload_id": "3fcb2ef4-b582-45c4-8677-47dca660611d"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
job_id | String | Replacement operation identifier. |
resource_type | String | room. |
resource_id | String | Existing room identifier, preserved by replacement. |
state | String | pending or running while background processing continues; succeeded when already completed. Read poll_url for the final succeeded or failed state. |
poll_url | String | GET operation-status URL, also returned in Location. Authenticate with the same user token. |
RESPONSE EXAMPLE
{
"job_id": "a68027ab-f7b3-46d3-9b6b-d78c8ef476a7",
"resource_type": "room",
"resource_id": "502",
"state": "pending",
"poll_url": "/api/rooms?id_tour=41&action=job_status&job_id=a68027ab-f7b3-46d3-9b6b-d78c8ef476a7"
}
HTTP 200 confirms that replacement processing was accepted. The response returns after the background processor acknowledges startup, without waiting for panorama processing. Read poll_url until state is succeeded or failed; errors after acceptance are reported by job_status and upload_status. During processing, the room state is processing and it is temporarily excluded from the viewer. The original visibility is restored after success. Room ID, name, initial view, north offset, order, annotations, protections, POIs and navigation links are preserved. Changing the panorama does not move or realign existing hotspots. Read the room again after completion to get its current ETag.
Repeat the same Idempotency-Key, body and original If-Match after a timeout; do not start a separate replacement blindly. A stale version or reused upload is rejected. Permissions, account, plan, storage target and quota are rechecked during processing. If processing fails, the previous panorama and visibility are restored and new files and reservations are cleaned up. Subsequent API requests mark abandoned processing as failed and restore the previous panorama once the processing deadline has passed and no panorama generator is still active. After failure and cleanup, initiate a new upload.
Old files are removed only after successful processing and only when no other room, alternate panorama or language panorama references them. If old-file cleanup fails, replacement remains successful; upload/job status reports cleanup_pending=true and subsequent API requests retry cleanup. Old storage remains reserved until cleanup completes. Both original files must fit the available storage during replacement, including when the room count limit has been reached.
Rename room, set initial position and compass north
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour; requires edit_room. |
action | No | String | update (default). |
id_room | Yes | String or integer | Room identifier. |
name | No | String | Room name, maximum 255 characters; cannot be empty. |
yaw | No | Number | Horizontal direction of the initial view, in degrees. Must be finite. Rounded to the nearest integer, with halves away from zero, and wrapped into -180 to 180. |
pitch | No | Number | Vertical direction of the initial view, in degrees. Must be finite. Rounded to the nearest integer, with halves away from zero, and clamped to -90 to 90. |
northOffset | No | Integer | Compass north offset, from 0 to 360 degrees inclusive. |
Supply at least one of name, yaw, pitch or northOffset. Omitted fields keep their current values; null and boolean values are not accepted for these fields. The initial position controls the view when entering this room. Changing yaw or pitch also invokes the tour loading-background generator when applicable. North can be changed independently. These updates do not consume a room slot or upload reservation.
REQUEST EXAMPLE
{
"id_tour": "41",
"id_room": "502",
"yaw": 35,
"pitch": 0,
"northOffset": 90
}
To rename only, send id_tour, id_room and name. To change only north, send id_tour, id_room and northOffset.
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
id / tour_id | String | Room identifier and parent tour identifier. |
name / type | String | Room name and media type. |
state | String | processing, ready, failed or deleting. |
visible | Boolean | True when the room is included in the viewer, subject to the tour access restrictions. |
priority | Integer | Room order. |
yaw | Integer | Saved horizontal direction of the initial view, in degrees. |
pitch | Integer | Saved vertical direction of the initial view, in degrees. |
northOffset | Integer | Saved compass north offset, in degrees. |
job_id | String or null | Current operation identifier. |
preview_url | String or null | Authenticated JPEG preview URL; null until ready. |
panorama_url | String or null | Authenticated URL for the room image stored in viewer/panoramas/ (not the original upload or multiresolution tiles). Send Authorization: Bearer USER_TOKEN. Null unless the image room is ready; a missing or invalid file returns 409 PANORAMA_NOT_READY. |
etag | String | Quoted room version; also returned in the ETag header for detail/update. |
Updated room with the saved yaw, pitch and northOffset values, and its ETag response header. If-Match must contain the room ETag. After a timeout, read the room again to check the saved values before retrying with its current ETag. A stale If-Match returns 412 without applying changes.
Set room order and initial room
BODY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour; requires edit_room and edit_tour. |
action | Yes | String | set_order. |
room_ids | Yes | Array | Complete ordered list of room IDs, without duplicates; maximum 32768 entries. |
initial_room_id | Yes | String or integer | Ready room included in room_ids. |
REQUEST EXAMPLE
{
"action": "set_order",
"id_tour": "41",
"room_ids": [
"502",
"503"
],
"initial_room_id": "502"
}
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
Updated tour object and ETag response header; fields match GET tours / Tour detail. If-Match must contain the tour ETag.
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. For update, provide at least one editable field; yaw and pitch must be finite numbers, and northOffset must be an integer from 0 to 360. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or invalid. Supply 8-128 characters using letters, digits or ._:-, and keep the same key when retrying the same operation. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. PLAN_EXPIREDThe account responsible for this operation has an expired subscription. Renew it before starting or completing panorama replacement. PAYMENT_REQUIREDThe subscription has an invalid payment status. Resolve the billing issue before creating additional resources. PROFILE_INCOMPLETEThe user profile is missing required account information. Complete the profile, then repeat the operation. PLAN_CONFIGURATION_REQUIREDThe account has no usable plan configuration. An administrator must assign or repair the plan before additional resources can be created. UNSUPPORTED_TOUR_TYPEThis upload and room-creation operation is unavailable for external tours and AR simulator tours. Select a standard tour. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe requested resource does not exist, is outside your access scope, belongs to another tour or user, or is no longer available. Check the identifiers and parent tour; this response does not reveal whether another user owns it. |
|
| CODE 409 | REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. ROOM_NOT_READYThe room is processing, being deleted or awaiting file cleanup. Wait for completion, check upload/job status and read the current room ETag before starting a separate operation. UNSUPPORTED_ROOM_TYPEThis operation requires an image room with a supported JPEG or PNG panorama filename. Video, HLS and Lottie rooms cannot be replaced by this operation. UPLOAD_NOT_READYThe upload has not completed image validation. Complete the upload and check its status; create the room only after the upload reaches ready. UPLOAD_ALREADY_CONSUMEDThis upload has already been used to create a room and cannot be reused or cancelled. Retry the original creation with its original Idempotency-Key to retrieve that result, or use another upload for another room. UPLOAD_STATE_CONFLICTThe upload is not in the required state or was initiated for a different purpose or room. Start replacement uploads with id_room and use them only for that room; do not use a new-room upload for replacement. STORAGE_QUOTA_EXCEEDEDThe tour owner account does not have enough unreserved storage for this operation. Release unused uploads, remove stored content or increase the storage allowance. PROCESSING_CAPACITY_UNAVAILABLEThe server cannot reserve enough physical space for the upload, temporary files and generated images. Retry after capacity becomes available or ask the administrator to free server storage. STORAGE_TARGET_CHANGEDThe tour's storage destination changed during processing, so the operation cannot finish against that destination. Ask the administrator to confirm the storage configuration and pending cleanup before restarting the upload. IDEMPOTENCY_CONFLICTThis Idempotency-Key was already used with different request data. Restore the original body for a retry; use a different key only for a separate operation. IDEMPOTENCY_KEY_EXPIREDThe stored response for this key has passed its replay deadline, so the key cannot be reused. Check the resource status before starting any separate operation with a different key. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. PROCESSING_FAILEDThe panorama or its derived images could not be generated successfully. Read the operation error and cleanup_pending status. For unexpected processing exceptions, details.processing_diagnostic_id identifies a server PHP log entry containing the original error; internal messages and paths are not returned by the API. Resolve the cause and complete cleanup before starting another upload; retrying the failed operation returns its recorded error. PROCESSING_TIMEOUTAn image generator exceeded its time limit and was stopped. Check operation and cleanup status, then ask the administrator to review processing capacity or timeout settings before starting another upload. PROCESSING_INTERRUPTEDThe operation was interrupted before completion, exceeded the abandoned-job recovery deadline, or was interrupted repeatedly. The failed operation is not resumed. Check operation and cleanup status, then start another upload after the cause is resolved and cleanup completes. PROCESSOR_UNAVAILABLEThe server cannot start the panorama processor. PHP_PATH is optional: PHP is located using the server PATH, then PHP_BINARY with the PHP-FPM path converted to its CLI counterpart, then PHP_BINDIR/php or the php command. Candidates are verified by actually starting PHP CLI, including when open_basedir restricts filesystem checks. Startup notices do not invalidate the CLI check. If detection fails, error.details.processor_attempts lists the candidate source, exit_code, reason and detected sapi when available. Reasons include spawn_failed, startup_timeout, command_not_found, not_executable, process_failed, missing_php_probe_output and not_cli. error.details.processor_diagnostic_id identifies the matching entry in the server PHP error log, where attempted paths and startup output are recorded; those paths and output are not included in the API response. If PHP_PATH is configured, it must start PHP CLI. An administrator must check PHP availability, required process functions and panorama-processing configuration. Processor startup is checked before creating an upload. Room creation and panorama replacement also require the background PHP process to acknowledge startup within three seconds; this checks startup only and does not wait for image processing. Failure to launch or acknowledge startup fails the operation and triggers cleanup. A previously failed operation keeps its stored error: after correcting the server, initiate a new upload with a new Idempotency-Key; replaying the failed operation does not restart processing. TEMPORARY_STORAGE_UNAVAILABLEThe server cannot create, read or write the private upload files. An administrator must check free space and directory permissions. Check upload status before retrying; a failed upload must be restarted. |
|
| CODE 412 | VERSION_CONFLICTThe If-Match value does not match the resource's current ETag. Read the resource again, reconcile your changes and repeat the update with the returned ETag. |
|
| CODE 428 | PRECONDITION_REQUIREDThe required If-Match header is missing. Read the room or tour first and send its exact quoted ETag with the update or deletion request. |
|
| CODE 422 | INVALID_ROOM_SETroom_ids must contain every room in the tour exactly once, and initial_room_id must name a ready room in that list. Read the room list again and submit the complete ordered set. CHECKSUM_MISMATCHThe received byte count or SHA-256 does not match the declared part or complete file. Verify the file bytes, part boundaries and checksums. If validation has already failed, start another upload with the corrected data. |
|
| CODE 410 | UPLOAD_EXPIREDThe upload has reached its fixed 24-hour expiration time and no longer accepts writes. Start another upload and transfer the file again. | |
{
"error": {
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "a1b2c3",
"retryable": false,
"details": {}
}
}
DELETE ROOM
DELETE https://vt.pageier.com/api/rooms
HEADERS
| Field | Required | Type | Description |
|---|---|---|---|
Authorization | Yes | String | Bearer USER_TOKEN. Use the access token returned by login. |
If-Match | Yes | String | The exact quoted ETag returned for the resource. Missing: 428 PRECONDITION_REQUIRED; stale: 412 VERSION_CONFLICT. |
QUERY PARAMETERS
| Field | Required | Type | Description |
|---|---|---|---|
id_tour | Yes | String or integer | Parent tour; requires delete_room. |
id_room | Yes | String or integer | Ready image room to delete. No request body or action parameter. |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 204 | No content | |
Empty body after deletion and cleanup. The upload-to-room association is released while the upload record and deletion receipt are retained. A repeated request with the same original If-Match returns 204 after authorization checks, including when the numerical room ID has since been reused. To delete a room currently using that ID, read its current ETag and send that value. Old cleanup and deletion operations cannot modify a different room that reuses the identifier.
ERROR RESPONSES
| Field | Type | Description |
|---|---|---|
| CODE 400 | INVALID_REQUESTA required field is missing, an unknown field was supplied, a value has the wrong format, or the JSON body cannot be read. Check this endpoint's parameter names, types, filters and allowed values. UNKNOWN_ACTIONThe action value is not supported for this endpoint and HTTP method. Use one of the actions listed in its request parameters. |
|
| CODE 401 | SESSION_EXPIREDThe user access token has expired. Refresh the session if a valid refresh token is available; otherwise sign in again before repeating the request. SESSION_REVOKEDThe user token is missing, invalid, revoked or no longer matches the account or session. Sign in again and use the token returned by that sign-in. |
|
| CODE 403 | ACCOUNT_INACTIVEThe user account or the account responsible for this tour is inactive. An authorized administrator must reactivate it before the operation can proceed. PERMISSION_DENIEDThe authenticated user does not have permission to perform this action. Ask the account administrator to grant the required permission or choose a resource you can manage. |
|
| CODE 404 | RESOURCE_NOT_FOUNDThe requested resource does not exist, is outside your access scope, belongs to another tour or user, or is no longer available. Check the identifiers and parent tour; this response does not reveal whether another user owns it. |
|
| CODE 409 | REQUEST_IN_PROGRESSAnother request is already working on the same resource or holding a required lock. Wait for the Retry-After interval, then repeat the same request with the same Idempotency-Key when applicable. ROOM_NOT_READYThe room is processing, failed or being deleted, so the requested action is unavailable. Read the room and operation status, then wait for ready or resolve the processing error. UNSUPPORTED_ROOM_TYPEThis deletion operation supports ready image rooms with a supported panorama file. The selected room has an unsupported media type or file. Select a supported image room. |
|
| CODE 426 | HTTPS_REQUIREDThe request was sent without accepted HTTPS transport. Call the HTTPS API URL; an administrator must also configure trusted proxy handling if TLS is terminated by a proxy. |
|
| CODE 429 | RATE_LIMITEDToo many requests were sent within the allowed interval. Wait for the Retry-After value in seconds, then retry; reduce the request rate if this recurs. |
|
| CODE 500 | INTERNAL_ERRORThe server encountered an unexpected error. Keep request_id for support. Before retrying a creation, check whether it completed and reuse the original Idempotency-Key. |
|
| CODE 503 | API_SETUP_REQUIREDThe API configuration or required database schema is unavailable. An administrator must complete or repair the API setup; changing the request parameters will not resolve this error. SERVICE_UNAVAILABLEA required database or storage service could not complete the operation. Retry later with the same Idempotency-Key where applicable. If the problem persists, provide request_id to the administrator. |
|
| CODE 412 | VERSION_CONFLICTThe If-Match value does not match the resource's current ETag. Read the resource again, reconcile your changes and repeat the update with the returned ETag. |
|
| CODE 428 | PRECONDITION_REQUIREDThe required If-Match header is missing. Read the room or tour first and send its exact quoted ETag with the update or deletion request. |
|
{
"error": {
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "a1b2c3",
"retryable": false,
"details": {}
}
}
GET TOUR STATISTICS
Retrieve the statistics of a desired tour of the authenticated user.
GET https://vt.pageier.com/api/tour_stats
AUTHORIZATION
All
Authorization: Bearer USER_TOKEN. The Authorization header also accepts Bearer API_KEY together with the user token parameter.
QUERY PARAMETERS
| Field | Mandatory | Type | Description |
|---|---|---|---|
token |
When using API_KEY | String | User token returned by login. Omit when Authorization contains Bearer USER_TOKEN. |
id_tour |
Integer | The id of the tour (default all the tours) | |
type |
Enum | The type of statistics chosen from all, unique (default all) |
RESPONSE
| Field | Type | Description |
|---|---|---|
| CODE 200 | Ok | |
data |
Array | Contains the statistics of the tour |
| CODE 404 | No tour access records match the requested tour or current selection, so statistics are unavailable. Check id_tour and whether the tour has recorded any visits. | |