Documentation Index

Fetch the complete documentation index at: https://docs.ssw.splashtop.com/llms.txt

Use this file to discover all available pages before exploring further.

Manage Private Applications by API

Prev Next

This document describes how to use Splashtop Secure Workspace API automation to create, enable, validate, and remove Private Applications programmatically.

Use case

Some deployment workflows create short-lived preview environments with unique hostnames, for example:

preview-<SHA>.example.com

Because Secure Workspace applications do not support wildcard application URLs, each preview hostname must be registered as its own Private Application. Instead of creating these applications manually, a CI/CD pipeline can call the Secure Workspace APIs during provisioning and cleanup.

Recommended workflow:

  1. CI/CD deploys the preview environment.
  2. CI/CD creates a matching Secure Workspace Private Application.
  3. CI/CD assigns the application to the expected connector, edge, network, and user groups.
  4. CI/CD enables the application.
  5. Optional: CI/CD validates access with the sdpc command-line client.
  6. When the preview environment is removed, CI/CD deletes the Secure Workspace application.

API capabilities

The automation flow uses these endpoints:

Step API Purpose
Authenticate API key POST /iam/v1/apikey/authenticate Exchange an API key for SDP and IAM tokens.
Import application POST /ztna/v1/application/import_json Create Private Applications from an exported/import JSON payload.
Export application POST /ztna/v1/application/export_json Export application definitions. Useful for templates and validation.
Get application by name GET /ztna/v1/application/by_name?app_name={app_name} Find an existing application by its unique application name.
Check application name POST /ztna/v1/application/check_name Check whether one or more application names are already in use.
Enable or disable application PUT /ztna/v1/application/{app_id}/enabled Enable or disable application access.
Delete application DELETE /ztna/v1/application/{app_id} Remove a Private Application.
Bulk delete applications DELETE /ztna/v1/bulkDeleteApplication Remove multiple applications by ID.

The script uses the import/export JSON APIs because they provide a practical way to clone a known-good application definition and replace the environment-specific fields.

Required API key permissions

Create a dedicated API key for automation. Grant only the permissions required by the pipeline.

Minimum permissions for the full lifecycle:

Permission Required for
ImportApplication Creating applications with import_json.
ExportApplication Exporting application templates or validating created applications.
EnableApplication Enabling or disabling application access.
RemoveApplication Deleting applications during cleanup.
DesktopConnectApplication Optional, only if the pipeline validates connectivity with sdpc.
DesktopGetApplication / DesktopListApplication Optional, only if validation needs desktop application lookup/list behavior.

Store the API key in the CI/CD secret manager. Do not commit it to source control.

Inputs required by the pipeline

The pipeline needs these values:

Input Description Example
SSW_SERVER_URL Secure Workspace server hostname. qaautotest.us.ssw.splashtop.com
SSW_API_KEY API key with the permissions listed above. Stored as a CI/CD secret.
ORG_NAME Organization name. The script accepts this argument, although the organization is normally resolved from the server URL and API key context. qaautotest
ENV Runtime environment used by the test script when downloading sdpc. prod
CONNECTOR_NAME Connector where the Private Application should be assigned. prod-qa-auto-testing-kind-connector
EDGE_NAME Edge name used by the application. Portland, Oregon
NETWORK_NAME Network name shown in the application definition. qaautotest
APP_NAME Unique internal application name. preview-a1b2c3d4
DISPLAY_NAME User-facing application name. Preview App a1b2c3d4
TARGET_HOST Preview environment hostname or internal service DNS name. preview-a1b2c3d4.example.com
TARGET_PORT Application port. 443
PROTOCOL Application protocol. https, http, ssh, etc.
GROUP_NAMES Groups allowed to access the application. ["_alluser"] or a customer-specific group.

Available production edge names:

  • Ashburn, Virginia
  • Portland, Oregon
  • Frankfurt, Germany
  • London, England
  • Tokyo, Japan
  • HongKong, China
  • Taipei, Taiwan

Example test-script invocation:

sudo bash -x auto_app_testing.sh "${TEST_APIKEY}" \
  qaautotest.us.ssw.splashtop.com \
  qaautotest \
  prod \
  prod-qa-auto-testing-kind-connector \
  qaautotest \
  "Portland, Oregon"

Authentication

Exchange the API key for an SDP token:

curl --location --request POST "https://${SSW_SERVER_URL}/iam/v1/apikey/authenticate" \
  --header "Content-Type: application/json" \
  --data "{\"apikey\":\"${SSW_API_KEY}\"}"

The response includes:

{
  "data": {
    "sdp_token": "<SDP_TOKEN>",
    "iam_token": "<IAM_TOKEN>"
  }
}

Use data.sdp_token as the bearer token for application management APIs:

Authorization: Bearer <SDP_TOKEN>

Application import payload

The import API expects a multipart request with:

Form field Description
connector Connector name to assign to the imported application.
edge Edge name to assign to the imported application.
inclusion_list[] Optional list of application names to import from the file. Leave empty to import all apps in the file.
exclusion_list[] Optional list of application names to skip.
file JSON file containing app_data_list.

Example import JSON:

{
  "export_metadata": {
    "tenant_name": "example-tenant",
    "org_name": "example-org",
    "user_name": "automation",
    "export_time": "2026-06-26T00:00:00Z"
  },
  "app_name_list": [
    "preview-a1b2c3d4"
  ],
  "app_data_list": [
    {
      "name": "preview-a1b2c3d4",
      "display_name": "Preview App a1b2c3d4",
      "edge_name": "Portland, Oregon",
      "site_name": "prod-qa-auto-testing-kind-connector",
      "site_display_name": "prod-qa-auto-testing-kind-connector",
      "network_name": "qaautotest",
      "protocol": "https",
      "group_names": [
        "_alluser"
      ],
      "hosts": [
        {
          "host": "preview-a1b2c3d4.example.com",
          "port": 443
        }
      ],
      "host": "preview-a1b2c3d4.example.com",
      "port": 443,
      "ports": [
        443
      ],
      "ip": "",
      "access_type": "all",
      "app_web_config": "{\"host\":\"preview-a1b2c3d4.example.com\",\"port\":\"443\",\"via_private_network\":true,\"recording\":true}",
      "icon": "web",
      "via_private_network": true,
      "enabled": true,
      "dynamic_target_flag": false
    }
  ]
}

Notes:

  • name must be unique within the organization. The import API is not an upsert API. If an application with the same name already exists, import fails.
  • connector and edge in the multipart form can override the values in the JSON file.
  • group_names must reference groups that exist in the organization. If a group is missing, the import result may include an error message for that application.
  • app_web_config is a JSON string, not a nested JSON object.
  • For private applications, set via_private_network to true.

Create application

Create the application with import_json:

curl --location --request POST "https://${SSW_SERVER_URL}/ztna/v1/application/import_json" \
  --header "accept: application/json" \
  --header "Authorization: Bearer ${SDP_TOKEN}" \
  --form 'inclusion_list[]=""' \
  --form 'exclusion_list[]=""' \
  --form "connector=${CONNECTOR_NAME}" \
  --form "edge=${EDGE_NAME}" \
  --form "file=@import_apps.json"

Successful response shape:

{
  "code": 0,
  "data": [
    {
      "result": "success",
      "app_id": "<APPLICATION_ID>",
      "app_name": "preview-a1b2c3d4",
      "err_messages": []
    }
  ]
}

Save data[0].app_id. The pipeline needs it for enable, validation, and cleanup.

To avoid duplicate-name failures, the pipeline can check the name first:

curl --location --request POST "https://${SSW_SERVER_URL}/ztna/v1/application/check_name" \
  --header "Authorization: Bearer ${SDP_TOKEN}" \
  --header "Content-Type: application/json" \
  --data "{\"app_names\":[\"${APP_NAME}\"]}"

Enable application

Enable the application after creation:

curl --location --request PUT "https://${SSW_SERVER_URL}/ztna/v1/application/${APP_ID}/enabled" \
  --header "Authorization: Bearer ${SDP_TOKEN}" \
  --header "Content-Type: application/json;charset=UTF-8" \
  --data '{"enabled":true}'

To disable an application, send:

{"enabled": false}

Export an application template

Exporting an existing known-good application is the easiest way to build a template:

curl --location --request POST "https://${SSW_SERVER_URL}/ztna/v1/application/export_json" \
  --header "Authorization: Bearer ${SDP_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{"app_names":["template-private-app"]}' \
  --output app_template.json

The CI/CD pipeline can then transform the exported JSON by replacing:

  • app_name_list
  • app_data_list[].name
  • app_data_list[].display_name
  • app_data_list[].host
  • app_data_list[].hosts
  • app_data_list[].port
  • app_data_list[].ports
  • app_data_list[].app_web_config
  • app_data_list[].group_names
  • app_data_list[].site_name
  • app_data_list[].edge_name
  • app_data_list[].network_name

Delete application

Delete the application when the preview environment is removed:

curl --location --request DELETE "https://${SSW_SERVER_URL}/ztna/v1/application/${APP_ID}" \
  --header "Authorization: Bearer ${SDP_TOKEN}"

For batch cleanup:

curl --location --request DELETE "https://${SSW_SERVER_URL}/ztna/v1/bulkDeleteApplication" \
  --header "Authorization: Bearer ${SDP_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{"id_list":["<APP_ID_1>","<APP_ID_2>"]}'

Recommended cleanup behavior:

  • Store the created app_id as a deployment output or CI/CD artifact.
  • Apply a naming convention such as preview-<service>-<sha> so orphaned apps can be identified.
  • Retry deletion on transient errors.
  • Run a scheduled cleanup job to remove old preview applications that match the naming convention.

Optional connectivity validation

The test script downloads the latest sdpc client for the current operating system, starts a local application tunnel, then checks whether the expected local port is open.

Example:

sdpc \
  -sdp_server "https://${SSW_SERVER_URL}" \
  -app_ports "{\"${APP_NAME}\":[9876]}" \
  -apikey "${SSW_API_KEY}" \
  "${APP_NAME}"

Then verify the local port:

nc -zv 127.0.0.1 9876

This step is optional for provisioning. It is useful in automated tests because it verifies that:

  • the application was created,
  • the application is enabled,
  • the API key can access the application,
  • the connector path works,
  • the target host and port are reachable.

CI/CD integration pattern

Suggested provisioning flow:

deploy preview environment
  -> authenticate API key
  -> generate import_apps.json from template
  -> import Secure Workspace application
  -> save app_id
  -> enable application
  -> optionally validate with sdpc

Suggested teardown flow:

load saved app_id
  -> authenticate API key
  -> delete Secure Workspace application
  -> remove preview environment

If app_id is not available during teardown, use the preview naming convention to find the matching application first, then delete it. Avoid relying only on display name because users may change display names manually.

curl --location --request GET "https://${SSW_SERVER_URL}/ztna/v1/application/by_name?app_name=${APP_NAME}" \
  --header "Authorization: Bearer ${SDP_TOKEN}"

Error handling

Recommended checks:

  • Authentication response contains data.sdp_token.
  • Import response has code: 0.
  • Import response contains a successful data[].result.
  • data[].app_id is not empty.
  • Enable request returns success.
  • Delete request returns success.

Common failures:

Failure Likely cause Resolution
Token response has no sdp_token Invalid API key or wrong server URL. Verify API key, tenant, environment, and server URL.
Import returns no permission API key lacks ImportApplication. Update API key permissions.
Enable returns no permission API key lacks EnableApplication. Update API key permissions.
Delete returns no permission API key lacks RemoveApplication. Update API key permissions.
Application already exists The application name is already used in the organization. Delete the old app first or generate a unique name.
Connector not found Multipart connector value does not match an existing connector in the org. Verify connector name and organization.
Edge not found Multipart edge value does not match an existing edge. Verify edge name.
Group not found group_names contains a group that does not exist. Create the group or update the payload.
Connectivity validation fails Connector path or target host/port is unreachable. Verify DNS, target service, connector, firewall, and port.

Security recommendations

  • Use a dedicated automation API key, not a personal administrator key.
  • Grant only the required permissions.
  • Store the API key in a CI/CD secret store.
  • Do not print the API key or bearer token in logs.
  • Use predictable but non-sensitive names for preview applications.
  • Clean up applications when preview environments are removed.
  • Periodically audit old preview applications by naming convention and creation time.

Reference implementation

See:

infrastructure/tools/auto_app_testing.sh

The script demonstrates:

  • exchanging an API key for SDP/IAM tokens,
  • generating an import JSON file,
  • importing a Private Application,
  • exporting the application for validation,
  • enabling the application,
  • downloading and running sdpc,
  • validating local connectivity,
  • deleting the application,
  • removing temporary files.