Appearance
Apps
An app adds an integration to a tenant, usually a vendor: actions that workflows and rules call, a webhook handler when the vendor answers later, and a hand-off when the applicant finishes a check on the vendor's own page. An app is a WebAssembly module. A tenant installs it, as often as it needs, each install with its own name, configuration and secrets, and the platform runs every call in a sandbox that holds that tenant's data for that one call.
Platform apps and tenant apps
A generic connector covers most vendors, model apps cover language models, and a tenant's own uploads cover the rest:
- Platform apps ship with each release, in the catalogue (
catalog/in the repository), and every tenant sees them. The catalogue holdshttp-api: a connector for any vendor's HTTP API, installed once per vendor with the vendor's endpoint, auth and secrets, through which each workflow step or rule sends a request of its own (the path, query, headers and body), in Hoppscotch's request format. Calling a vendor takes no code, whether it answers one request with its result or later through the install's signed webhook, and a step can send it the run subject's documents once the install's documents switch is on. See App catalogue. It also holds five model apps,claude,chatgpt,gemini,jevandopenai-compatible, which put a language model in a step or a rule: an answer to a prompt, as text or as JSON of the step's schema, or decisions on named questions with a probability a later step routes on. See AI models. - Tenant apps are modules a tenant's administrators write with the SDK and upload, for what
http-apicannot do: the applicant handed to the vendor's own page, a webhook signed with a public key, sent as a form, carrying several events or naming its session only in its URL, a document inside a JSON body, several requests in one call, or logic of the app's own. Only that tenant sees them, and they can be installed once published, after another administrator's approval when the tenant requires one. See Apps: authoring.
Whatever its kind, a tenant puts an app to use through installs. An install has a name of its own (vendor-a), unique in the tenant among its installs' names and the former names they keep: a rename keeps the former name working as an alias (reference). It runs one version of the app: the tenant picks the version, fills in the configuration the version declares, approves the hosts the install calls and stores the secrets its configuration requires. An install of a platform app can also upgrade on its own within its version's compatible range, waiting for an approval when the new version calls more hosts or reads more (reference). One app can be installed several times, for example once per vendor account (vendor-a and vendor-b), each install with its own configuration, secrets, webhook URL, circuit and history; a tenant has at most 50 installs. Two tenants never share an install, and two installs never share a configuration or a secret. Workflows call an install with a call_app step and rules with the app rule type, by the install's name and an action.
How a call runs
The app runner (apps/app-runner) is a service of its own, with no database connection, no storage credentials and no key to the secret store. The worker and the API hand it each call with everything the call needs: the module's hash, the install's configuration, its approved hosts, its secrets and its live cached values (decrypted for this call) and, for an install whose calls read documents, a token that expires in five minutes for the tenant's documents, a run's call for its subject's only. The runner fetches the module from the API by its hash, runs one export on an instance of the module no other call has used and discards it. Only what the module keeps in its install's cached values survives the call: the runner answers what the call changed of them, and the worker or the API stores it, sealed.
Inside the sandbox a module has no file system, no sockets and no network of its own. It reaches the outside through seven host functions, each bound to the call's tenant: http_request (to the install's approved hosts only), hmac, hmac_verify, document_read, log, cache (values the install keeps between its calls, such as a vendor's token) and ai (a model request the host sends with the install's keys filled in). A module names a secret with a placeholder and the host puts the value into the outgoing request, so the value never enters the module; the host also builds a request's authentication from the secrets (Basic, Bearer, API key, Digest, AWS Signature, Hawk, JWT, Akamai EdgeGrid) and its HMAC signatures. The host fills only the placeholders that carry the tag it draws for each call, so run data, a webhook's body or a vendor's answer that reads like one is sent as text and never names a secret; every string that leaves a call (an error, a log line, the module's answer) is cleared of the call's secret values, of what the host derived from them and of the cached values it read or set. See Apps: host interface.
Trust
A tenant that installs an app trusts its author with each install's configuration, secrets and documents, towards the hosts that install approved. An install's hosts are its version's and the exact hosts of the URLs its configuration names where the version allows one (hostsFromConfig): in production https only, without credentials or wildcards, a port only when it is not the default and never one browsers refuse, never an IP address or localhost, and never one of the deployment's own hosts at any port. A save that adds a host names it (approveHosts), waits for another administrator when the tenant requires approvals for apps, and is audited with its hosts either way; the console lists the hosts before every save that adds one. The runner refuses every other host or port and every private address whatever the name, and a step or a rule never chooses a host. A hand-off sends the applicant only to an https page on one of the install's hosts. Platform apps' hosts are reviewed with the catalogue; http-api declares none, so each of its installs reaches only the host of the endpoint its administrators approved, and a model app declares its vendor's API (openai-compatible declares none), to which an install's base URL adds the host it names. Documents reach an app only through an install whose calls read them: its version needs them and, where the version puts them behind a switch as http-api does, the install's configuration turns it on, a save audited either way. A run's step or rule then reads only its run subject's documents, and an http-api install sends them only to its one host. A module may keep values between its install's calls, sealed per install and never seen by another install or tenant, redacted from every answer, log line and record as [cache:NAME], cleared when the install's secrets, configuration or version change, and listed by name, with Clear, for its administrators (Cached values of an install). Those values are the install's, not a subject's: every call of the install reads them, whatever its subject, so a module that kept a subject's data there (a document's text, personal data) would carry it into the calls for other subjects, their vendor requests and their answers. A tenant trusts the author to keep only what the vendor gives the install there, such as a token or a session. Nothing lets an app reach another tenant, the platform's credentials or a host the install did not approve.
The deployment's operator, who acts through pnpm ops rather than a tenant's API, lists every tenant's app versions with the hosts they call and the installs that run them, never their configuration or secrets. The operator can review a tenant's own versions before they publish (a tenant under review, APP_REVIEW_DEFAULT for new tenants), and can block a module everywhere, by its hash or every version of its publisher's app, with a reason the tenants running it read: from the block's commit, with no restart, its calls, test calls, installs, upgrades, publishes and webhooks fail with app_blocked, and its sessions hand no applicant off (Operations). The operator can also cap a tenant's app calls, or the HTTP requests they make, per UTC day and month (pnpm ops tenants quota): the tenant reads its quotas in GET /apps/metering and cannot change them, and a call past one fails with app_quota_exceeded (Operations).
A tenant's version can carry Sigstore provenance: a signature over the module, or an attestation of the build that names it, which the tenant attaches after the upload (Apps: authoring). The API keeps what it verified, who signed and from which source, and shows whether the operator's trust policy names that signer; a deployment that requires it publishes, installs and upgrades to a tenant's version only once a trusted signer has signed it (Operations).
Turning apps on
Apps run when the deployment has object storage (the modules live there) and an app runner (APP_RUNNER_URL on the API and the worker). When either is missing, the API answers the /apps and /app-installs routes with 503 apps_unavailable, the health summary reports apps: off, and a call_app step fails with that reason. Deployment lists the settings: the runner's bearer token, the API's internal listener, the call-token secret, the catalogue directory and the secret store key.
Where to go
| You want to | Read |
|---|---|
| Call a vendor's HTTP API without writing an app | App catalogue: http-api |
| Ask a language model in a step or a rule | AI models |
| Write an app, test it and upload it | Apps: authoring |
| Look up a manifest field, a limit, a route or code | Apps: reference |
| Write a module without the TypeScript SDK | Apps: host interface |
| Install a platform app, or build the catalogue | App catalogue |
| Read the SDK's exports and types | App SDK reference |
| Manage apps in the console | Admin console: Connect |