Build your own setup screen
Your own setup screen is the Build it into your product option of Deployment UX: the customer starts the deployment from a page in your product and never sees the deployment portal. One API call creates the deployment and returns the setup step for the cloud and method the customer picked.
Example UI /Not deployed
Document processing
Settings · Deploy in your cloud
Deploy in your cloud
Run document processing in your own AWS account, next to your files.
What the customer does:
- Opens Deploy in your cloud in your product, picks a cloud and a region, and clicks Deploy.
- Runs the setup step your screen shows, with their own cloud credentials. On AWS that's one click into the AWS console.
- Comes back to your screen, which follows the deployment until it's ready.
What your backend does:
- Creates a setup link for the customer.
- Creates the deployment with the link's token. The response holds the setup step.
- Saves the deployment ID, and reads the deployment's status from then on.
What your screen can launch
The response's installation object depends on the setup method (setupMethod) you ask for:
| Setup method | Platforms | What your screen shows |
|---|---|---|
cloudformation | AWS | A button that opens installation.launchUrl: the AWS console's create-stack page, every parameter filled in. |
terraform | AWS, Google Cloud, Azure, Kubernetes | installation.terraform.hcl to save as main.tf, and the commands that apply it. |
helm | Kubernetes | installation.helm.values to save as values.yaml, and installation.helm.command. |
cli | AWS, Google Cloud, Azure | The setup CLI: installation.cli.installCommand, cli.configuration to save as deployment.toml, and cli.command. |
Only CloudFormation opens with one click. The others are commands the admin copies and runs.
Send these customers to the deployment portal instead, from the same setup link:
- Google Login. Google's sign-in returns only to the portal's domain, so your own page can't run it. Offer Terraform or the CLI on your page, or send Google Cloud customers to the portal.
- Linux servers. The join command for your customers' machines comes from the portal.
- Everything you don't build. Any cloud or method your screen doesn't offer.
Before you start
- Publish a release with
alien release, and wait until the setup packages for your methods are built. - Set up the API client from Add a Deploy button. The snippets import
alienandPROJECTfrom it.
Every Alien call runs on your server. The browser only talks to your backend.
Create the setup link
Server. When the customer clicks Deploy, create a setup link for their organization. Its setup token can create deployments for this one customer and nothing else.
POST /v1/deployment-groups/setup-links
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, // your immutable organization ID, from the session
name: org.slug,
maxDeployments: 1,
setupItems: [{ item: "deployment", required: true }],
})
return { setupToken: link.token, portalUrl: link.deploymentLink }
}- On click, not on render. Each call mints a new token.
- Organization from the session. Never take it from the request body.
- Keep
portalUrlfor the customers you send to the portal.
Map customers to deployments explains the external ID. Sizes and a network choice go on this same link: see Sizes and networks.
Create the deployment
Server. Create the deployment with the setup token, not your API key. setupHandoff: "await-registration" tells Alien the customer's setup step creates the infrastructure and reports back when it's done. installation asks for the setup step.
POST /v1/deployments
import { Alien } from "@alienplatform/platform-api"
import { PROJECT } from "./alien"
export type SetupChoice = {
platform: "aws" | "gcp" | "azure"
method: "cloudformation" | "terraform" | "cli"
region: string
}
export async function createDeployment(setupToken: string, name: string, choice: SetupChoice) {
// Acts for this one customer
const customer = new Alien({ apiKey: setupToken })
return customer.deployments.create({
name,
project: PROJECT,
platform: choice.platform,
setupItem: "deployment",
setupMethod: choice.method,
setupHandoff: "await-registration",
installation: { region: choice.region },
})
}The response for CloudFormation:
{
"deployment": { "id": "dep_0c29fq4a2yjb7kx3smwdgxlc", "status": "pending", "setupMethod": "cloudformation" },
"token": "…",
"installation": {
"method": "cloudformation",
"region": "us-east-1",
"stackName": "a3f9c21b-customer-1",
"launchUrl": "https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?…"
}
}tokenis the deployment token: the credential for this one deployment. The setup step keeps it, so the setup link can expire without breaking the deployment.- Kubernetes. Helm takes
installation.namespace. Terraform on Kubernetes also needsbasePlatform(aws,gcporazure). - Package still building. Right after a release, the call fails with
409INSTALLATION_NOT_READY. Show "Preparing the setup" and try again in a minute.
Show the setup step
Server. Your route creates the link and the deployment, and saves the deployment ID before it returns:
export async function POST(req: Request) {
const org = await currentOrg(req) // from the session, never from the body
const choice = parseSetupChoice(await req.json()) // allow only the clouds and methods you offer
const { setupToken } = await createSetupLink(org)
const created = await createDeployment(setupToken, org.slug, choice)
await db.orgs.update(org.id, { deploymentId: created.deployment.id })
const { installation, token, managementExternalId } = created
return Response.json({ installation, token, managementExternalId })
}Browser. For CloudFormation, open the tab before you wait for your backend, so popup blockers allow it:
export async function onDeployClick(choice: SetupChoice) {
const tab = choice.method === "cloudformation" ? window.open("about:blank", "_blank") : null
const res = await fetch("/api/deploy", { method: "POST", body: JSON.stringify(choice) })
if (!res.ok) { tab?.close(); throw new Error(await res.text()) }
const setup = await res.json()
if (tab) tab.location.href = setup.installation.launchUrl
return setup // Terraform and CLI: render the steps on your page
}For Terraform, show installation.terraform.hcl as main.tf, then the commands with the response's values:
export TF_VAR_token='<token>'
export TF_VAR_management_external_id='<managementExternalId>' # AWS only
terraform init && terraform applyFor the setup CLI, show cli.installCommand, cli.configuration as deployment.toml, and cli.command. cli.commandVariants holds the same commands for linux, mac and windows.
- The admin sees the token. It's in the launch URL and in the commands. That's expected: it acts only on this deployment. Show it only to signed-in members of this organization.
- Not the admin. The person who clicks often can't create IAM roles. Offer Send to an admin with the setup link (
portalUrl): the portal walks the admin through the same deployment.
Come back to it
Example UI /Waiting for setup
Document processing
Settings · Deploy in your cloud
Finish the setup in AWS
The setup hasn’t started in your AWS account yet. We’ll update this page when it does.
- Account
- 123456789012 · us-east-1
- Last checked
- 8 seconds ago
Show it when your saved deployment's status is pending and lastHeartbeatAt is null.
- Reload. Read the deployment ID you saved. Reopen setup calls
POST /v1/deployments/{id}/installation, which returns the same setup step and token. - Second click. Creating the deployment again with the same name and choice returns the same deployment and setup step. A CloudFormation retry keeps its stack name and release, even after you publish a new one.
- Changed choice. A second call with another cloud or method fails with
SETUP_RESERVATION_CONFLICT. Show the first choice's steps, or remove the pending deployment withaction: "forget"(see Other delete actions) before the customer picks again. - Form closed. The admin can close the AWS tab without creating the stack. Keep this state: nothing exists in their account yet.
- Stack building. On CloudFormation,
setupMetadata.setupReservation.stackStartedAtis set while AWS creates the stack. Show how long it has been building. - Stack rolled back.
setupMetadata.setupReservation.rolledBackAtis set. Ask the admin to delete the failed stack, where its Events tab shows why, and to start again.
{
"id": "dep_0c29fq4a2yjb7kx3smwdgxlc",
"status": "pending",
"setupMethod": "cloudformation",
"lastHeartbeatAt": null,
"setupMetadata": {
"setupReservation": { "stackStartedAt": "2026-01-15T08:52:10Z", "accountId": "123456789012", "region": "us-east-1" }
}
}When the setup step reports back, the deployment moves to provisioning. From there your screen shows the same states as every other option: see Show deployment progress.
Test it
- Each method you offer. Deploy into a test account with each one, and watch your page move from waiting to ready.
- Cancel the AWS form. Close the CloudFormation tab without creating the stack. Your page must stay on "Finish the setup in AWS".
- Reload during initial setup. Your page must show the same deployment, and Reopen setup the same stack name.
- Release, then click. Publish a release and click Deploy at once. Your page shows "Preparing the setup", then succeeds on a retry.
- A Google Cloud customer. Confirm your page sends them to the portal, or offers Terraform.
Next
- Show deployment progress: every state after initial setup starts
- Verify and activate: turn the feature on when it works
- Sizes and networks: Small, Medium, Large and an existing VPC
- Remove a deployment: when the customer leaves