Skip to contentSkip to Content
ReferenceActivepieces Access & Isolation

Activepieces Access & Isolation

This page is the reference for how AgentLane connects to Activepieces, what “an isolated instance for a partner” actually means in each tenancy mode, which endpoints drive provisioning and who may call them, and the exact pipeline each setup runs.

For the operational walkthrough, see Admin: Provision a Partner. For why Activepieces is the engine at all, see Workflow Engine.

Who can reach Activepieces

AgentLane itself reaches Activepieces only from inside the container network. Whether anyone else can is a deployment choice, and it defaults to no.

  • AgentLane always talks to Activepieces internally. ACTIVEPIECES_BASE_URL is a server-to-server address the API container resolves on the Docker network — http://activepieces-app:80 in production, http://localhost:8090 in development. Every admin call and every webhook trigger goes over that address. It is never a public URL.
  • The editor UI can optionally be published, and is not by default. Setting ACTIVEPIECES_DOMAIN records a hostname; the public Traefik router is written only when ACTIVEPIECES_PUBLIC_UI=true as well (deploy/scripts/deploy.sh). This is deliberately two switches rather than one: every deployment that runs Activepieces at all sets ACTIVEPIECES_ENABLED=true, and that alone used to publish the editor.

Publishing the editor puts a shared automation engine’s admin login on the public internet. In the default shared tenancy mode one project holds every partner’s flows, and Community Edition cannot scope a human user to one project — so anyone who reaches that login holding the platform-admin credential sees every tenant. If you turn it on, put an IP allowlist or basic auth in front of it (deploy/nginx/conf.d/activepieces.conf.template). The original design kept it internal-only, reached over an SSH tunnel.

  • Partners and clients never get Activepieces UI access. The Portal does not proxy the Activepieces editor. Instead, GET /partners/:id/automation/workflows/:workflowId/viewer returns a native, read-only view: the workflow’s template definition plus recent run history.
  • Every trigger originates from AgentLane. AutomationWebhookResolver composes the absolute webhook URL against the internal base URL at trigger time, so inbound business events hit the AgentLane API first and are dispatched inward from there.

The read-only viewer is a deliberate substitute, not a limitation we haven’t gotten to. Activepieces Community Edition cannot scope a human user’s access to a single project — in shared tenancy, handing a partner the real Activepieces UI would expose every other partner’s flows.

What isolation a partner actually gets

AUTOMATION_TENANCY_MODE selects between two models. Neither gives a partner their own Activepieces deploymentresolve_endpoint returns the same global ACTIVEPIECES_BASE_URL for every partner in both modes.

shared (default)project
Activepieces deploymentOne, shared by all partnersOne, shared by all partners
Activepieces projectOne, shared by all partners — the value of ACTIVEPIECES_PROJECT_IDOne project per partner, created on demand
Where flows liveAll partners’ flows in the same projectPartner’s own project
Isolation boundaryAgentLane’s database, exclusivelyAgentLane’s database, plus a project boundary
Name collisions prevented byNamespaced deterministic externalIdsNamespaced externalIds + separate project
deleteTenant on teardownNo-op (the shared project must survive)Deletes the partner’s project

In shared mode ensureTenant does not create anything — it returns the single configured ACTIVEPIECES_PROJECT_ID, the platform admin’s own default project. Activepieces CE has no “current user’s default project” concept, and every project-scoped endpoint requires an explicit project ID, so this value must be set or provisioning fails immediately with a clear error.

In shared mode, AgentLane’s authorization checks are the only thing separating one partner’s automation resources from another’s. There is no engine-level tenant boundary to fall back on. Treat any change to partner scoping in the API as a tenant-isolation change.

project mode is technically supported and exercised by ensureTenant (list by display name, create if absent), but shared remains the default. Before switching, run a capability matrix against the exact candidate image — a project ID alone does not establish tenant isolation.

How AgentLane authenticates

Not with a static API key. There isn’t one to use.

Activepieces CE has no durable server-to-server API key: POST /v1/api-keys is gated behind platform.plan.apiKeysEnabled, which defaults to false and requires a paid Enterprise license. Confirmed live against the pinned activepieces/activepieces:0.82.0 image — the route 404s even for the platform-admin user on an unlicensed instance.

The only Bearer token that authenticates /v1/projects, /v1/flows and /v1/app-connections on CE is a user login JWT. So ActivepiecesSessionAuthService:

  1. Signs in via POST /v1/authentication/sign-in using ACTIVEPIECES_ADMIN_EMAIL / ACTIVEPIECES_ADMIN_PASSWORD — a real Activepieces user created through that instance’s own first-run signup, which grants platform-admin rights to the first user automatically.
  2. Caches the resulting JWT (roughly a 7-day lifetime on the pinned build).
  3. Refreshes it proactively 10 minutes ahead of expiry, and reactively whenever the HTTP client sees a 401 and calls invalidate().

No static key is ever stored on the instance row: apiKeyEncrypted is written as null during provisioning. These admin credentials are full platform-admin access to every partner’s automation resources — keep them only in trusted infrastructure config, never in partner-visible settings.

Endpoint surface

All provisioning routes live under partners/:id/automation and are ADMIN-only, guarded by RolesGuard. The one exception is the viewer, which also accepts the owning PARTNER.

MethodRouteRolePurpose
GET/partners/:id/automationADMINInstance snapshot — tenancy mode, project ID, base URL, status
GET/partners/:id/automation/attemptsADMINProvisioning attempt history
GET/partners/:id/automation/stepsADMINPer-step breakdown of an attempt (defaults to the latest)
POST/partners/:id/automation/provisionADMINRun the provision pipeline. Body: { templateNames?, clientId? }. Returns 202
POST/partners/:id/automation/reprovisionADMINBody: { mode?: "repair" | "recreate", templateNames?, clientId?, refreshTemplates? }. Returns 202
DELETE/partners/:id/automationADMINDeprovision. Destructive
GET/partners/:id/automation/workflows/:workflowId/viewerADMIN, owning PARTNERRead-only template definition + recent runs
GET/partners/me/automationADMIN, PARTNERThe only provisioning route a partner may call. View-only, redacted

provision, reprovision and the deprovision route each write an audit log entry (automation_instance.provision_requested and siblings) recording the acting user.

What a partner is allowed to see

GET /partners/me/automation is deliberately redacted. A PARTNER caller never receives baseUrl, projectId, or lastError — those are infrastructure detail and, in shared mode, would describe resources shared with other partners. Instead the twelve internal steps collapse into four coarse stages:

Stage keyLabelInternal steps it covers
preparingPreparingfeature_gate, partner_lookup, claim_instance
connectingConnecting to your automation platformresolve_endpoint, verify_connectivity, ensure_tenant
configuring_accessConfiguring accessresolve_credentials, bind_credentials
installing_workflowsInstalling workflowsimport_flows, verify_flows, enable_flows, mark_ready

The response carries errorPublic — a client-safe message — never the internal error. That split is what lets a partner see a real “provisioning failed, here’s what to do” state without leaking infrastructure shape.

The setup pipeline

Provisioning runs synchronously within the request and is recorded step by step by AutomationProvisionStepRecorder. Each step is individually timed and records success, failure, warnings, and both an internal and a client-safe error.

feature_gate

Refuses to proceed unless ACTIVEPIECES_ENABLED=true. This is the documented rollback switch — setting it to false disables provisioning entirely.

partner_lookup

Loads the partner record and resolves the effective provider from Partner.automationProvider, falling back to AUTOMATION_PROVIDER.

claim_instance

atomicClaim takes exclusive ownership of the instance row for this attempt ID. This is what stops two concurrent provision requests from both proceeding.

resolve_endpoint

Returns ACTIVEPIECES_BASE_URL and writes it to the instance row. The same value for every partner, in both tenancy modes.

verify_connectivity

Health-checks the platform and fails the attempt unless the state is healthy — so a network or auth problem surfaces here rather than halfway through creating resources.

ensure_tenant

shared: returns the configured ACTIVEPIECES_PROJECT_ID, creating nothing. project: lists projects by display name and creates one if absent. The resolved project ID is written to the instance row.

resolve_credentials

Pre-flight only. Fails fast on an obviously-missing credential before touching Activepieces at all. Authoritative resolution happens in the next step.

bind_credentials

For each required connection in each template, upserts an Activepieces app connection keyed by a deterministic externalId. Idempotent by contract: retrying must reuse the existing provider credential, never create a duplicate. An unresolved credential fails the whole attempt rather than enabling a flow with a silent gap.

import_flows

Creates each catalog flow, then builds its action chain. Idempotent on re-run: if a providerFlowId is already recorded for the workflow, it calls updateFlow against the existing flow instead of importing a second copy.

Every imported flow also gets a terminal report step appended automatically, posting the run’s outcome to /executions/webhook. Without it an execution never reported completion: the row stayed RUNNING until the timeout job marked it FAILED, so a successful run showed as failed and its usage was never metered. It is marked continue-on-failure, because the conversation saga triggers these same flows without an executionId and reports through a different endpoint — that rejected report must not fail an otherwise good run.

The step uses @activepieces/piece-http 0.11.19, which sends only body.data (with body_type: "json"). Fields placed directly under body go out as an empty request, so the step and every POST step in the catalog templates wrap their JSON in data.

Flows provisioned before this existed do not have the step, and flows provisioned before the body.data fix send it with an empty body. Both are corrected on the next Reprovision → repair, which rebuilds the flow in place.

verify_flows

Confirms each imported flow exists and matches the template.

enable_flows

Publishes and activates the flows.

mark_ready

Sets the instance status to ready and stamps provisionedAt.

If any step throws, the instance is marked failed with the recorded error. Missing credentials produce a targeted message naming the offending steps: Could not activate: missing credential for step(s) ….

Repair, recreate and teardown

POST .../reprovision takes a mode:

Re-runs six steps against the existing resources, with no teardown:

feature_gateresolve_credentialsbind_credentialsupdate_flowsverify_flowsenable_flows

Use this when credentials were rotated or a flow definition changed.

Repair re-reads every requested template from the global catalog and replaces the partner’s stored copy (refreshTemplates defaults to true for repair). Pass refreshTemplates: false to keep the stored copies; a stored copy is still replaced whenever the catalog holds a newer templateVersion. Provisioning and recreate apply that version rule too. The import_flows step’s metadata lists the version imported for each template.

Deprovision is destructive and irreversible. Prefer recreate when the goal is simply “start over”.

Instance states

unprovisioned ──Provision──► provisioning ──► ready └──failure──► failed ──Provision (retry)──► provisioning ready ──Reprovision (repair)───► provisioning (rebuilds in place, no teardown) ready ──Reprovision (recreate)─► provisioning (full teardown, then full provision) ready ──Deprovision────────────► unprovisioned (permanent)

There is no separate “retry” action — provisioning a failed instance re-runs the same pipeline.

A run that crashes mid-flight would otherwise leave the row stuck in provisioning or deprovisioning forever, so AutomationProvisionTimeoutJob reaps anything older than AUTOMATION_PROVISION_TIMEOUT_MINUTES (default 15).

Configuration

VariableDefaultNotes
ACTIVEPIECES_ENABLEDfalseMaster switch. Must be true to provision. Only the exact string true enables it
ACTIVEPIECES_BASE_URLInternal container address. Never a public URL, no https:// requirement
ACTIVEPIECES_ADMIN_EMAILPlatform-admin login used for session auth
ACTIVEPIECES_ADMIN_PASSWORDAs above. Full platform-admin access — trusted infrastructure only
ACTIVEPIECES_PROJECT_IDRequired in shared mode. Provisioning fails without it
ACTIVEPIECES_EDITIONceInformational only today
AUTOMATION_PROVIDERactivepiecesGlobal default when Partner.automationProvider is unset
AUTOMATION_TENANCY_MODEsharedshared or project
AUTOMATION_CALLBACK_SECRETSigns execution callbacks back into AgentLane, and derives each agent’s public ingest token
AUTOMATION_INTERNAL_API_URLhttp://api:4000Internal address a flow calls AgentLane back on. Never public
AUTOMATION_HEALTH_TIMEOUT_MS5000verify_connectivity budget
AUTOMATION_PROVISION_TIMEOUT_MINUTES15Stuck-attempt reaper threshold

Rolling back to a non-Activepieces posture is ACTIVEPIECES_ENABLED=false, which trips feature_gate on every subsequent attempt.


Activepieces Access & Isolation — AgentLane