Build from Your Own CI/CD Pipeline
For the complete MongoDB documentation index, see www.mongodb.com/docs/llms.txt
Build from Your Own CI/CD Pipeline
MongoDB Atlas Agent Engine is in Public Preview and intended for evaluation and prototyping purposes only. We do not recommend production deployments during preview and expect to communicate breaking changes with you periodically. To learn more about preview feature considerations, see Preview Features.
Overview
In this guide, you can learn how to build and deploy an agent from your own CI/CD system. You can use this approach instead of the Atlas Agent Engine GitHub webhook pipeline.
You can deploy from any CI/CD system that can send an HTTPS request with a header, including Drone, GitHub Actions, GitLab CI, and Jenkins.
The Atlas Agent Engine does not require a vendor-specific integration or the agentengine CLI to build and deploy. Your pipeline implements the same build and deploy sequence as the CLI by using a project-scoped service account as its credential.
Tip:
To build and deploy an agent from the CLI instead, see [Build the Agent Image](https://www.mongodb.com/docs/agentengine/deploy/agent-image.md#std-label-agent-engine-deploy-agent-image and [Deploy Your Build.](https://www.mongodb.com/docs/agentengine/deploy/deploy-build.md#std-label-agent-engine-deploy-build
Endpoint Format
Most endpoints in this guide are scoped by project and workspace, in the following format:
https://agentengine.mongodb.com/api/v1/projects/<project-id>/workspaces/<workspace-id>/...
Before You Begin
Your pipeline uploads the agent source as an archive, which requires a workspace whose source type is archive. A GitHub-connected workspace rejects the first call in the sequence with a 409
UNSUPPORTED_SOURCE_TYPE error. A push triggers the builds for that workspace, not an uploaded archive.
To create an archive-source workspace, run the following command from your agent's directory:
agentengine init --org-id <org-id> --project-id <project-id>
The agentengine init command registers a new workspace and scaffolds local development tooling. Use the workspace ID that it prints in the path of every endpoint in this guide. To learn more about this command, see [Register Your Agent.](https://www.mongodb.com/docs/agentengine/build/create-project.md#std-label-agent-engine-agentic-init
If the agent already has a GitHub-connected workspace, you do not need to migrate it. You have the following options:
-
Continue to use the webhook pipeline for that workspace.
-
Create a second, archive-source workspace for the same agent and target it from your pipeline. You can maintain both workspaces simultaneously.
Set Up Pipeline Credentials
The requests in this guide authenticate by using an access token from a project-scoped service account. Include the token in the Authorization header of each request, in the following format:
Authorization: Bearer <access-token>
Replace the <access-token> placeholder with your service account access token. To create a service account, run the following command:
agentengine service-account create ci-pipeline \
--project-id <project-id> \
--role PROJECT_OWNER \
--description "CI pipeline" \
--secret-expires-in 720h
The --secret-expires-in flag specifies the lifetime of the client secret. To avoid use of an exposed credential in your CI/CD system, rotate the secret on a schedule that you define. To learn more about service accounts, see [Manage Service Account Credentials.](https://www.mongodb.com/docs/agentengine/api-keys-service-accounts.md#std-label-agent-engine-api-keys-service-accounts
Important:
The command displays the client secret only once. You can't retrieve it again. If you lose access to the secret, rotate it to create a new one.
Store the client ID and client secret as secrets in your CI/CD system. Do not write them inline in a pipeline file or commit them to source control.
To obtain an access token from the client ID and client secret, send a POST request to the /api/v1/oauth/token endpoint:
curl -s "https://agentengine.mongodb.com/api/v1/oauth/token" \
-X POST \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials"
The response contains an access_token field. The access token is valid for one hour, so request a new token at the start of each pipeline run. To learn more, see [Request an Access Token.](https://www.mongodb.com/docs/agentengine/api-keys-service-accounts.md#std-label-agent-engine-service-account-token
Rotate Pipeline Credentials
The Atlas Agent Engine doesn't rotate service account secrets automatically. To rotate your pipeline's secret, run the agentengine service-account rotate command and update the secret in your CI/CD system.
The previous secret remains valid for up to seven days or until its expiration, whichever comes first. During that period, your pipeline continues to authenticate while you update the secret. To revoke the previous secret early, rotate the secret a second time or deactivate the service account. To learn more, see [Manage Service Account Credentials.](https://www.mongodb.com/docs/agentengine/api-keys-service-accounts.md#std-label-agent-engine-api-keys-service-accounts
Example Pipeline
Regardless of your CI/CD system, the pipeline has the following shape. You can implement every step by using curl and jq:
check out your repository
-> archive your agent directory
-> POST builds/archive-init (save build_id and upload_url)
-> PUT upload_url (the archive)
-> POST builds/<build-id>/start
-> GET builds/<build-id> (until the status is terminal)
-> POST deployments (with build_id)
Build and Deploy Sequence
A build and deploy sequence includes the following steps:
-
Checking out and archiving your agent source
-
Initializing the build archive
-
Uploading the agent source
-
Starting the build
-
Polling the build status
-
Deploying the build
In this section, you can learn how to implement each request in your pipeline.
- Check out and archive your agent source.
Check out the commit or branch that you want to build, then create a tar.gz archive of your agent directory. The Atlas Agent Engine builds exactly the archive that you upload, so this step determines what the build contains.
The following example uses the tar command to create an archive named agent.tar.gz. The example excludes local development artifacts from the archive, including the .venv, __pycache__, and .agentengine directories and the .env file.
bash
tar --exclude='.venv' --exclude='__pycache__' \
--exclude='.agentengine' --exclude='.env' \
-czf agent.tar.gz -C <agent-directory> .
- Initialize the build archive.
Before running the following commands, set these environment variables in your CI/CD job:
bash
export PROJECT_ID="<project-id>"
export WORKSPACE_ID="<workspace-id>"
export ACCESS_TOKEN="$CI_ACCESS_TOKEN"
export COMMIT_SHA="<commit-sha>"
Use the project ID for your Atlas Agent Engine project. Use the workspace ID returned by agentengine init. Set CI_ACCESS_TOKEN to the access token that you requested from the service account in your CI/CD job, as described in [Set Up Pipeline Credentials](https://www.mongodb.com/docs/agentengine/deploy/ci-cd.md#std-label-agent-engine-ci-cd-credentials. Set COMMIT_SHA to the commit that you pass in git_info.commit_sha.
To create a build record and get a presigned URL for the source upload, send a POST request to the builds/archive-init endpoint by running the following curl command:
bash
curl -s -X POST \
"https://agentengine.mongodb.com/api/v1/projects/$PROJECT_ID/workspaces/$WORKSPACE_ID/builds/archive-init" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"label": "my-ci-build-123",
"git_info": {
"commit_sha": "<commit-sha>",
"branch": "<branch-name>",
"dirty": false
},
"build_target": { "subdirectory": "" },
"auto_deploy": false
}'
All fields in the request body are optional. To make the build reproducible, always pass the git_info.commit_sha property. Without it, the Atlas Agent Engine tags the resulting image build-<random-id> instead of sha-<commit-sha>.
To have a successful build deploy itself and skip the final step, set auto_deploy to true.
If the request succeeds, the endpoint returns a 201 Created response that resembles the following:
json
{
"build_id": "bld_01ABC...",
"upload_url": "https://<presigned-s3-url>",
"upload_expires_at": "2026-01-01T00:05:00Z",
"source_type": "archive"
}
- Upload the agent source.
To upload your agent directory, send a PUT request that includes the archive as a tar.gz file to the upload_url value returned from the preceding step by running the following curl command:
bash
curl -s -X PUT \
--upload-file agent.tar.gz \
-H "Content-Type: application/gzip" \
"$UPLOAD_URL"
Do not send an Authorization header on this request. The presigned URL is the credential, and it expires at the time given by the upload_expires_at value returned by archive-init. If the upload URL expires before you use it, call archive-init again to get a new upload URL.
If the upload succeeds, the endpoint returns a 200 OK response.
- Start the build.
To confirm that the upload arrived and to queue the build job, send a POST request with no body to the builds/<build-id>/start endpoint and save the response by running the following curl command:
```bash RESPONSE=$(curl -s -X POST \ "https://agentengine.mongodb.com/api/v1/projects/$PROJECT_ID/workspaces/$WORKSPACE_ID/builds/$BUILD_ID/start" \ -H "Authorization: Bearer $ACCESS_TOKEN")
echo "$RESPONSE" ```
If the request succeeds, the endpoint returns a 202 Accepted response that resembles the following:
json
{ "build_id": "bld_01ABC...", "status": "accepted" }
If a build for the same commit is already running, this endpoint returns a 409 BUILD_ALREADY_ACTIVE error. The error is a duplicate build guard rather than a transient failure.
When available, the response includes the ID and status of the active build in the details object:
json
{
"success": false,
"code": "BUILD_ALREADY_ACTIVE",
"details": {
"existing_build_id": "bld_01ABC...",
"existing_status": "in_progress"
}
}
Set BUILD_ID to the details.existing_build_id value and poll that build instead of initializing another build archive:
bash
BUILD_ID=$(printf '%s' "$RESPONSE" |
jq -r '.details.existing_build_id // empty')
If the response does not include existing_build_id, list the workspace builds and select the active build for the same commit:
bash
BUILD_ID=$(curl -s \
"https://agentengine.mongodb.com/api/v1/projects/$PROJECT_ID/workspaces/$WORKSPACE_ID/builds?limit=100" \
-H "Authorization: Bearer $ACCESS_TOKEN" |
jq -r --arg commit "$COMMIT_SHA" '
.builds[]
| select(.commit_sha == $commit)
| select(
.status == "queued" or
.status == "in_progress" or
.status == "waiting"
)
| .build_id' |
head -n 1)
- Poll the build status.
To wait for the build to finish, poll the builds/<build-id> endpoint on a regular interval until the build reaches a terminal status. The following example polls every five seconds and fails the pipeline after 30 minutes, so that a build stuck in the queued or waiting state can't keep your CI/CD job running indefinitely:
```bash TIMEOUT_SECONDS=1800 INTERVAL_SECONDS=5 ELAPSED=0
while true; do STATUS=$(curl -s \ "https://agentengine.mongodb.com/api/v1/projects/$PROJECT_ID/workspaces/$WORKSPACE_ID/builds/$BUILD_ID" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ | jq -r '.status')
if [ "$STATUS" = "succeeded" ]; then
break
elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "cancelled" ]; then
echo "Build $BUILD_ID ended with status: $STATUS" >&2
exit 1
elif [ "$ELAPSED" -ge "$TIMEOUT_SECONDS" ]; then
echo "Timed out after ${TIMEOUT_SECONDS}s waiting for build $BUILD_ID" >&2
exit 1
fi
sleep "$INTERVAL_SECONDS"
ELAPSED=$((ELAPSED + INTERVAL_SECONDS))
done ```
When the build reaches succeeded, the loop exits and the pipeline continues to the next step. When it reaches failed or cancelled, or when the timeout elapses, the example exits with a nonzero status so the pipeline fails. Adjust TIMEOUT_SECONDS and INTERVAL_SECONDS to match your build duration and your CI/CD system's tolerance for API calls.
While the build is in progress, the endpoint returns a response that resembles the following:
json
{
"build_id": "bld_01ABC...",
"status": "in_progress",
"executor_type": "vm",
"image_uri": null,
"lockfile_mode": null,
"error_message": null
}
The following table describes the status values:
| Status | Description |
|---|---|
queued |
The build is waiting to start. Continue polling. |
in_progress |
The build is running. Continue polling. |
waiting |
Another build currently holds the workspace build slot. Continue polling. |
succeeded |
The build completed and image_uri contains the image. Continue to the next step. |
failed |
The build did not complete. The error_message field identifies the root cause where the Atlas Agent Engine can determine it, such as an out-of-date lock file. |
cancelled |
A user or the Atlas Agent Engine canceled the build. |
The lockfile_mode field reports whether the Atlas Agent Engine honored a committed lock file. To learn more, see [Manage Dependencies.](https://www.mongodb.com/docs/agentengine/deploy/ci-cd.md#std-label-agent-engine-ci-cd-dependencies
- Deploy the build.
To deploy the image that the build produced, send a POST request to the deployments endpoint by running the following curl command:
bash
curl -s -X POST \
"https://agentengine.mongodb.com/api/v1/projects/$PROJECT_ID/workspaces/$WORKSPACE_ID/deployments" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "build_id": "'"$BUILD_ID"'" }'
The build_id field is optional. If you omit it, the Atlas Agent Engine deploys the most recent successful build in the workspace.
If the request succeeds, the endpoint returns a 202 Accepted response that resembles the following:
json
{ "deployment_id": "deploy-abc123" }
The response confirms only that the Atlas Agent Engine accepted the deployment. To confirm that the deployment is running correctly, poll the deployments/current endpoint, as shown in the following example:
bash
curl -s \
"https://agentengine.mongodb.com/api/v1/projects/$PROJECT_ID/workspaces/$WORKSPACE_ID/deployments/current" \
-H "Authorization: Bearer $ACCESS_TOKEN"
The endpoint returns the workspace's active deployment, including its status, the readiness of each component that the deployment runs, and the deployment's overall health. The following response is trimmed to the fields that a scripted check needs:
json
{
"deployment_id": "deploy-abc123",
"status": "successful",
"components": [
{
"name": "agent",
"available": true,
"replicas": 1,
"ready_replicas": 1
}
],
"health": {
"available": true,
"checked_at": "2026-01-01T00:10:00Z"
}
}
In a healthy deployment, status is successful, every entry in the components array has available set to true with ready_replicas equal to replicas, and the health object's available field is true.
How the Platform Uses git_info
The git_info object stores metadata describing the source you already checked out. It does not tell the Atlas Agent Engine what to build. The archive sequence never clones or checks out a repository, and it does not contact GitHub. The Atlas Agent Engine builds exactly the archive that you upload.
Selecting the commit or branch to build happens entirely in your pipeline, before you initialize the build archive. Your pipeline checks out the target ref, archives that working directory, and passes commit_sha and branch to label the resulting build. These values have the following effects:
-
commit_shadetermines the image tag and enables the duplicate build guard that the build start request applies. -
branchis recorded for display.
Manage Dependencies
If you commit a uv.lock file, the Atlas Agent Engine installs exactly that locked dependency set and does not re-resolve dependencies. The build response reports lockfile_mode as honored. If the lock file is out of date or cannot be honored, the build fails with an actionable error_message value rather than silently installing different versions. To resolve this failure, run uv lock locally, commit the updated lock file, and build again.
If you do not commit a lock file, the Atlas Agent Engine resolves dependencies on every build and reports lockfile_mode as re-resolved. This behavior is not an error.
Note: Lock File Limitation
The Atlas Agent Engine does not yet honor a committed lock file for VM-optimized executor builds. Those builds always re-resolve dependencies, regardless of a committed lock file. To check whether this limitation applies to your build, inspect the executor_type field in the build-status response. A value of vm indicates a VM-optimized executor build. A value of container indicates a container executor.
Do Not Declare the SDK Packages
Do not list the Atlas Agent Engine SDK packages as dependencies in your pyproject.toml file, including the following packages:
-
agentengine-langgraph -
agentengine-core -
runner-shared -
agentengine-memory
These packages are not published to a package index. Declaring one causes uv lock to fail with a not found in the package
registry error. The Atlas Agent Engine always installs these packages into your agent's environment as prebuilt wheels in a separate step, regardless of what your pyproject.toml file declares. Your agent can import them at runtime without declaring them. Declare only your agent's own dependencies.
Troubleshooting
The following table describes errors that you might encounter when you build and deploy from your own pipeline:
| Error | Likely cause |
|---|---|
409 UNSUPPORTED_SOURCE_TYPE on archive-init |
The workspace is GitHub-connected rather than archive-source. Create an archive-source workspace. |
409 BUILD_ALREADY_ACTIVE on start |
A build for this commit is already running. Use details.existing_build_id from the error response as BUILD_ID. If the response does not include that field, list the workspace builds and select the active build for the same commit. Poll that build instead of calling archive-init again. |
403 on every write request |
The service account doesn't have enough rights on the project. Use a service account with deployment management rights. |
403 with a project mismatch reason |
The service account's project doesn't match the project ID in the request path. |
400 INVALID_BUILD_STATE on deployments |
The referenced build has not succeeded, or it doesn't exist. |
409 DEPLOY_IN_PROGRESS on deployments |
A deployment is already in progress for this workspace. |
403 or an expired-URL error on the archive upload |
The presigned URL expired. Call archive-init again to get a new URL. |
A build failure that reports an out-of-date uv.lock file |
Run uv lock locally, commit the updated lock file, and build again. |
| `agent-engine-sdk-langgraph was not found in the package | |
registry| Remove the SDK package from the dependencies in yourpyproject.toml` file. |
Next Steps
After deploying your agent, you can monitor its performance and activity. To learn how to monitor your agent, see the [Monitor](https://www.mongodb.com/docs/agentengine/manage/monitor.md#std-label-agent-engine-monitor guide.
