Docs

Add a Deploy button

A Deploy button is the Launch from your product option of Deployment UX: your settings page starts initial setup, the deployment portal runs it, and your page shows the result. Customers deploy on their own, without waiting for your team to send a link.

Example UI /Not deployed

Document processing

Settings · Deploy in your cloud

Not deployed

Deploy in your cloud

Run document processing in your own AWS account, next to your files.

DeploySend to an admin

Show it when your own database has no deployment for this organization, or its last deployment reached deleted or retained.

  • Page load. Don't create a setup link to render this card. Create it when the customer clicks Deploy.
  • Lost ID. If you have no saved deployment but a deployment may exist, look the group up by external ID before you show this card.
  • Not the admin. The person who clicks often can't create IAM roles. Send to an admin creates a link to copy instead of opening it.
  • Second organization. Members of another organization must never see this customer's link. Resolve the organization from the session.
GET /v1/deployments?project=docproc&deploymentGroup=dg_r27ict8c7vcgsumpj90ackf7b
{
  "items": [],
  "nextCursor": null
}

The button needs the deployment portal: a branded setup page Alien hosts for the customer's admin. The flow:

  1. The customer opens Settings → Deploy in your cloud in your product and clicks Deploy.
  2. Your backend creates a setup link, a URL that sets up a deployment for this one customer.
  3. The browser opens the deployment portal, and the customer's admin runs initial setup in their own cloud.
  4. Back on your page, your backend reads the deployment's progress from Alien and shows it.

Sequence diagram. Lanes: Customer browser (your settings page), Your backend (your product), Alien (api.alien.dev). Steps: 1 · click Deploy; 2 · create setup link; 3 · portal link; 4 · open the portal; admin runs initial setup in their cloud; 5 · read progress; 6 · show state.

The sections below follow build order, and each says where its code runs: your server, the browser, or the Alien dashboard. The example is a document-processing service in a project called docproc.

Set up a client

This runs on your server. Create an API key in the dashboard under Settings → API Keys, or from the CLI:

alien api-keys create --for deployments

Keep the key on your server: it can create setup links and read every deployment in the project. Your frontend calls your backend, and your backend calls Alien.

lib/alien.ts
import { Alien } from "@alienplatform/platform-api"

// Server-side only. Create the key with: alien api-keys create --for deployments
export const alien = new Alien({ apiKey: process.env.ALIEN_API_KEY, workspace: "acme" })
export const PROJECT = "docproc"

TypeScript uses @alienplatform/platform-api. Python and Rust call the REST API directly; the examples use httpx and reqwest.

The integration uses the credentials below. Only the setup token ever reaches the customer:

CredentialWhere it livesWhat it can do
Alien API key (ax_…)Your serverCreate setup links, read status, retry, resize. Removal needs a project.admin key: alien api-keys create --for project-admin
Setup tokenIn the setup link, and in the CloudFormation Token parameterSet up deployments for one customer. It can't read other customers or your project
Gateway keysYour serverCall the hosted gateways at runtime: --for encryption-gateway, --for ai-gateway, --for remote-bindings, --for remote-sandbox

Map customers to deployments

Alien keeps one deployment group per customer: its record of that customer's deployments, setup links, keys, and models. You pick the group by an external ID, your own ID for the customer.

Use the ID of whatever owns the deployment in your product: usually the organization, sometimes a workspace or environment if one customer deploys more than once.

  • Use an immutable ID, such as org_8f2k, not a name or a slug. A renamed organization must still find its deployment.
  • Resolve it from the session on your server. Never accept it from the request body: anyone could deploy into, or read, another customer's group.
  • It's case-sensitive, up to 128 characters, and matches ^[A-Za-z0-9][A-Za-z0-9._~-]*$.

This runs on your server, in the route your Deploy button calls. It's one request, POST /v1/deployment-groups/setup-links, which finds or creates the customer's group and returns a link for it:

lib/setup-link.ts
import { alien, PROJECT } from "./alien"

export async function createSetupLink(org: { id: string; slug: string }) {
  const link = await alien.setupLinks.create({
    project: PROJECT,
    externalId: org.id, // from the session, never from the request
    name: org.slug,
    maxDeployments: 1,
    setupItems: [{ item: "deployment", required: true }],
  })
  return { deploymentGroupId: link.deploymentGroup.id, url: link.deploymentLink }
}

Save deploymentGroup.id on your organization record. Every later step reads the deployment through it.

A few things to get right here:

  • Create the link when the user clicks, not when the settings page renders. Each call mints a new token; a page render that creates links leaves a trail of valid ones nobody used.
  • A second click is safe. The same external ID returns the same group with a new link. The customer still ends up with one deployment, because maxDeployments: 1 caps the group.
  • An expiry is safe. expiresAt only stops new setups. Each deployment gets its own token at initial setup, so an expired or revoked link doesn't affect deployments it created. See Link lifetime.
  • Restrict choices only when you must. Without a policy, the portal offers every platform the release supports. Link options lists allowedPlatforms, allowedSetupMethods, inputs, and defaults.
  • Give the admin a way back. returnUrl adds a Back to link to the top of the portal, pointing at your settings page. It's a link, not a redirect, and it carries no state.
  • Recover expired links. refreshUrl is where the portal sends an admin who opens an expired, revoked, or disabled link. That route must sign the user in before it creates a new link.

Both URLs must be absolute https:// URLs of at most 2,048 characters, with no credentials in them.

The link has the form https://<portal domain>/deploy#<token>. The token sits in the URL fragment, so browsers don't send it to servers in requests or Referer headers.

Send the admin to the portal

This runs in the browser. Your route returns the link, and your page opens it in a new tab. Keep your settings page open in the first tab: that's where the customer comes back.

components/deploy-button.tsx
async function onDeploy() {
  const res = await fetch("/api/byoc/setup-link", { method: "POST" })
  const { url } = await res.json()
  window.open(url, "_blank", "noopener")
  setState("waiting-for-setup")
}

The portal is white-labeled: your logo and colors, and your domain once you add one (Brand the portal). The admin picks a platform and an Install method, and runs it with their own credentials. Deployment portal walks through it.

Plan for the person who clicked not being the cloud admin. Often they're a team lead who can't create IAM roles.

Next to Deploy, offer Send to an admin: it creates a link the same way and shows it to copy, with one line on what the admin will do. Anyone with the link can deploy for this customer, so show it only to signed-in members of the organization.

When they come back

No state comes back to your product. The portal never redirects to your page on its own, and its Back to link carries no result.

From the browser, you can't tell whether the admin ran initial setup, cancelled, or is still in their cloud console. Read the state from Alien.

When your settings page loads, your server lists the group's deployments with GET /v1/deployments?project=…&deploymentGroup=…:

lib/find-deployment.ts
import { alien, PROJECT } from "./alien"

const GONE = ["delete-pending", "deleting", "deleted", "retained"]

// Created when the admin starts initial setup in the portal. Null until then.
export async function findDeployment(deploymentGroupId: string) {
  const { items } = await alien.deployments.list({
    project: PROJECT,
    deploymentGroup: deploymentGroupId,
  })
  return items.find((d) => !GONE.includes(d.status)) ?? null
}

No deployment yet means the admin hasn't started initial setup. Show Waiting for setup, with Reopen setup (a new link) and Send to an admin. Don't show "Deployed" because the portal tab closed.

A deployment in pending means the admin started initial setup, and it hasn't reported back from their cloud yet. Waiting for setup covers that state.

If you lost the group ID, GET /v1/deployment-groups/by-external-id?project=docproc&externalId=org_8f2k finds it (alien.deploymentGroups.getDeploymentGroupByExternalId in TypeScript).

Track the deployment

This runs on your server. Once a deployment exists, read it with GET /v1/deployments/{id} and store what you need in your own database. Your settings page reads that, never Alien directly.

Example UI /Provisioning

Document processing

Settings · Deploy in your cloud

Setting up

Setting up in your AWS account

This usually takes 10 to 20 minutes. You can leave this page.

Resources

Compute3Setting up
  • Compute clustercomputeCreating

    Waiting for machines to join the cluster.

  • API serverapiWaiting

    Waiting for Compute cluster.

  • Event processoreventsRunning
Data1Running
  • Uploads bucketuploadsRunning
Secrets and keys2Running
  • Service credentialscredentialsRunning
  • Application secretssecretsRunning
Networking and access3Running
  • Networkdefault-networkRunning
  • Management identitymanagementRunning
  • Service account (application)application-saRunning
Last update
12 seconds ago

Show it when status is provisioning, waiting-for-machines, or waiting-for-secrets. Group resources[] by type, and show each one's displayName, status, and progress.

  • Polling. Poll every 10 to 30 seconds while someone has the page open, and every few minutes from a background job otherwise.
  • API errors. Back off, double the delay up to two minutes, and keep showing the last state you read.
  • Long setups. A first deployment usually takes 10 to 20 minutes. Say so once, and let the customer leave the page.
  • Missing secrets. waiting-for-secrets needs the customer: name the secret from runtimeMetadata.deployerSecrets and link to where they write it.
  • No percentages. Resources finish in parallel. Show statuses, not a progress bar or an ETA.
GET /v1/deployments/{id}
{
  "id": "dep_0c29fq4a2yjb7kx3smwdgxlc",
  "status": "provisioning",
  "platform": "aws",
  "resourceObservedAt": "2026-01-15T09:04:48Z",
  "resources": [
    {
      "id": "compute",
      "displayName": "Compute cluster",
      "type": "compute-cluster",
      "status": "provisioning",
      "progress": "Waiting for machines to join the cluster.",
      "error": null,
      "retryAttempt": 0
    }
  ]
}

Show deployment progress maps every status and the per-resource rows. Errors and retries and Updates and connection cover what comes after.

Activate

running means Alien finished. It doesn't prove your product can reach the service.

Run your own check from your backend, such as a request to the deployment's health endpoint, and store Active in your own database when it passes. Switch traffic to the deployment only then.

Read the deployment's endpoints with GET /v1/deployments/{id}/info (alien.deployments.getInfo). See Verify and activate for the full check.

Remove

Give customers a Remove button from the first release. Removal has up to three parts:

  1. A request to Alien.
  2. A wait while Alien deletes what it created.
  3. Sometimes, a last step the customer's admin finishes in their cloud.

See Remove a deployment.

Test it

  • Run the whole flow with a test organization: click Deploy, set it up in a test account, come back to your page, and watch every state until Active.
  • Close the tab during initial setup and reopen your settings page. It must show Waiting for setup or the deployment's progress, never a fresh empty state.
  • Click Deploy twice and finish initial setup from the second link. Your page must show one deployment.
  • Sign in as another organization and confirm it can't see or create links for the first.
  • Remove it and confirm your page reaches Removed only after the deployment is deleted.

Next

On this page