Skip to main content
These recipes chain the endpoints in the sidebar. All of them assume the Authorization: Bearer <api_token> header described in Getting Started, and a base URL of https://sdk.localops.co.

Onboard a bring-your-own-cloud (BYOC) customer - headless

The end to end recipe for a BYOC edition: your product running inside the customer’s own AWS, GCP or Azure account. Every step below is an API call, so onboarding a customer is a script you run once per customer rather than a walk through the console. The recipes further down this page cover each step in more detail.

Provision the environment in their cloud

POST /v1/environments with the customer’s cloud_connection_id, the project_id to file it under, and the region they want - aws-us-east-1, gcp-us-central1, and so on. Pass custom_cidr when their network team needs the VPC in a particular range.Then poll GET /v1/environments/{envId} until state is available.
POST /v1/environments is badged COMING SOON and returns 404 today - create the environment in the console for now. Every step after this one is live.

Add your services

POST /v1/environments/{envId}/services once per service - API, workers, cron jobs, frontend. Each one can come from a GitHub or GitLab repo, a Docker image or a Helm chart, with its own replica_count, CPU and memory.Keep each data.service.id - there is no list endpoint to look them up again. Adding a service to a customer later is the same call.See Create a Helm service and deploy it and Deploy a git service from CI.

Set their secrets

PUT /v1/environments/{envId}/secrets for values every service in the environment shares, and PUT /v1/environments/{envId}/services/{serviceId}/secrets for the ones scoped to a single service.This is where per customer configuration lives - their database URLs, their API keys, their license terms.See Update secrets safely - these writes replace the whole set.

Deploy

POST /v1/environments/{envId}/services/{serviceId}/deploy with a commit_id, a docker_image_tag or a helm_chart_version, then poll GET /v1/deployments/{deploymentId} until success or failed.See Deploy a git service from CI.

Attach their domain

POST /v1/environments/{envId}/services/{serviceId}/custom-domains returns the exact DNS records for the customer to create. Once they resolve, POST .../custom-domains/{customDomainId}/verify checks them and cuts traffic over.See Attach a custom domain.

Hand over the network details

GET /v1/environments/{envId} returns the ingress_host to point DNS at, the public_nat_ips to allowlist and the svc_role_arn to name in their resource policies - the three things the customer’s network and security teams ask for during review.See Point your own DNS and allowlists at an environment.

Ship a release to every customer

Once customers are onboarded, a rollout is the same call against each environment id:
Each call returns a deployment_id, so you can gate the next customer on the previous one reaching success and stage the rollout across your install base.
Keep a record of every customer’s envId and serviceId as you create them. The API has no list endpoint, so these identifiers are the only handle you have on a customer’s environment.

Point your own DNS and allowlists at an environment

GET /v1/environments/{envId} reads three fields live from the environment’s infrastructure on every call. They are what you need to connect your own DNS, firewalls and cloud IAM to a LocalOps environment.
All three are empty until the environment is provisioned, so poll until the one you need is set.
svc_role_arn is an identifier, not a credential. Naming it in a resource policy grants nothing until you also set up the trust in your own account. And public_nat_ips can change, so re-read it after infrastructure changes rather than caching it indefinitely.
To have LocalOps manage a domain for you instead, with certificates and verification, use Attach a custom domain below.

Create a Helm service and deploy it

Create the service

POST /v1/environments/{envId}/services with source: "helm_chart" and deploy_now: false.Keep deploy_now off for Helm services. It deploys without a chart version, so an explicit deploy is almost always what you want.
Keep data.service.id from the response - there is no list endpoint to look it up again.

Write service secrets

PUT /v1/environments/{envId}/services/{serviceId}/secrets, if the service needs any.

Deploy a chart version

POST /v1/environments/{envId}/services/{serviceId}/deploy with {"helm_chart_version": "1.4.0"}.Keep data.deployment_id from the response.

Poll the deployment

GET /v1/deployments/{deploymentId} until state is success or failed.
Service names on create accept letters and spaces only. Digits, hyphens and underscores are rejected. Updates are laxer and also allow hyphens.

Deploy a git service from CI

For a GitHub or GitLab service, deploy the head of the configured branch by sending an empty object:
Or pin an exact commit, with an optional note of up to 300 characters:
Then poll GET /v1/deployments/{deploymentId}. The deployment row is created synchronously, so you always get an id back immediately even though the rollout itself runs in the background - a rollout failure shows up as state: "failed", not as an error on the deploy call. For a docker_image service, send docker_image_tag instead. For helm_chart, send helm_chart_version.

Deploy a pull request preview

Check the parent service

The parent service must use the github source, be of type web, and have enable_previews turned on. Your plan must include preview environments, otherwise the call returns 403.Turn previews on with PATCH /v1/environments/{envId}/services/{serviceId} and {"enable_previews": true}.

Trigger the preview deploy

commit_id, branch and pr_number are all required in preview mode, and docker_image_tag and helm_chart_version must be absent.

Use the returned preview service

The response carries service_id, service_name, origin, pr_number and is_new - not a deployment id.A preview service is created for the pull request the first time (is_new: true) and reused on later calls (is_new: false). It inherits the parent’s type, resources, port and node group, and has auto_deploy turned on.

Poll the preview service

GET /v1/environments/{envId}/services/{serviceId} with the returned service_id, until state is running.

Attach a custom domain

Register the domain

POST /v1/environments/{envId}/services/{serviceId}/custom-domains with {"domain": "app.example.com"}.The response contains dns_records, each with record_name, record_type and record_value.

Create the DNS records

Add every returned record in your DNS provider - both the certificate validation record and the traffic routing record.

Verify

POST /v1/environments/{envId}/services/{serviceId}/custom-domains/{customDomainId}/verify.
While DNS is still propagating, verification fails with 500 and error_code: unknown rather than a 4xx. Retry once the records have propagated.

Confirm

GET /v1/environments/{envId}/services/{serviceId}/custom-domains until active_domain.state is deployed.Verifying a new domain retires the service’s previously active domain.
If you have verified the domain yourself and the live DNS check keeps failing, force-deploy is the escape hatch:
It deploys the domain without checking DNS and points the service at it. Because it skips verification, every call is written to the audit log as CUSTOM_DOMAIN_FORCE_DEPLOY.

Wire one service to another

Services inside the same environment reach each other over an in cluster DNS alias.

Read the alias

GET /v1/environments/{envId}/services/{serviceId} returns svc_alias. It is empty until the service is provisioned, so poll until it is set.

Publish it as a secret

PUT /v1/environments/{envId}/services/{serviceId}/secrets on the consuming service, with the alias as the host - for example DB_HOST.

Redeploy the consumer

Secret writes do not roll themselves out. Trigger a deployment for the consuming service.
The same response carries ops_dependencies - the cloud resources the service declares in its ops.json. Each one lists the environment variables it injects, so you can discover what a service already gets without reading its repo:
The $... tokens are resolved when the variables are injected into your container. The API returns them as declared.

Update secrets safely

Both secret endpoints are a full replacement, not a merge. Any key you leave out of the array is removed.

Read

GET /v1/environments/{envId}/secrets, or the service level equivalent.

Modify

Change or add entries in the array you just read. Keys must be non empty and unique - a duplicate is rejected with 422 and Duplicate secret key: <key>.

Write the whole set back

PUT the complete array. Validation runs before anything is written, so a rejected request changes nothing.

Delete a service

Remove protection

A protected service returns 409 on delete. Clear it first with PATCH /v1/environments/{envId}/services/{serviceId} and {"is_protected": false}.
When patching a service whose type is job, always include "auto_deploy": false in the body. Omitting it on a job typed service surfaces as a 500.

Delete

DELETE /v1/environments/{envId}/services/{serviceId} returns 202 {"message": "accepted"}, meaning the teardown was accepted and started. A non 2xx means nothing was torn down.

Poll to completion

GET /v1/environments/{envId}/services/{serviceId} through delete_queued, deleting and deleted, or delete_failed on failure.