PHASE 14 OF 15

REST API

Automate TeamCity with the REST API — authentication, all key endpoints, triggering builds, downloading artifacts, querying test results, managing agents, and practical curl-based scripts

REST APIcurlAutomationLocatorsBuilds
1

REST API Overview

AttributeValue
Base URLhttp://SERVER:8111/app/rest (or https://...)
API version in 2017.2.2v10 (default), v9 (legacy). Use /app/rest/10.0/ for explicit versioning.
Response formatJSON (default) or XML. Use Accept: application/json header.
Content type for POST/PUTContent-Type: application/json
Locator syntax

TeamCity REST API uses locators — flexible filter expressions — to identify resources:

# Single dimension locator:
builds?locator=buildType:BackendServices_Build

# Multi-dimension locator (comma-separated):
builds?locator=buildType:BackendServices_Build,branch:main,status:SUCCESS

# Dimensions:
# id:N             — by internal ID
# number:N         — by build number
# buildType:ID     — by build config ID
# project:ID       — filter by project
# status:SUCCESS|FAILURE|ERROR|UNKNOWN
# branch:name      — by branch name
# running:true     — only running builds
# personal:false   — exclude personal builds
# count:N          — limit results
2

Authentication

# Method 1: Bearer token (recommended — from Phase 13)
TC_TOKEN="your-api-token"
TC_URL="https://ci.yourcompany.com"

curl -H "Authorization: Bearer $TC_TOKEN" \
     -H "Accept: application/json" \
     "$TC_URL/app/rest/server"

# Method 2: HTTP Basic with username:password
curl -u "username:password" \
     -H "Accept: application/json" \
     "$TC_URL/app/rest/server"

# Method 3: Guest (read-only, no credentials)
curl -H "Accept: application/json" \
     "$TC_URL/guestAuth/app/rest/builds?locator=count:5"
ALWAYS USE BEARER TOKENS

API tokens (Bearer auth) are the safest option: they can be revoked without changing the user's password, they bypass 2FA (acceptable for automation), and they show which token was used in the audit log. Create a dedicated service account (ci-bot) and generate tokens under it.

3

Projects Endpoint

# List all projects
GET /app/rest/projects

# Get a specific project
GET /app/rest/projects/id:BackendServices

# List build configs in a project
GET /app/rest/projects/id:BackendServices/buildTypes

# Create a project (POST)
POST /app/rest/projects
Content-Type: application/json
{
  "name": "NewService",
  "id": "NewService",
  "parentProject": { "id": "BackendServices" }
}

# Delete a project
DELETE /app/rest/projects/id:OldProject
4

Build Types Endpoint

# List all build configurations
GET /app/rest/buildTypes

# Get a specific build configuration
GET /app/rest/buildTypes/id:BackendServices_Build

# List parameters of a build config
GET /app/rest/buildTypes/id:BackendServices_Build/parameters

# Set a parameter on a build config
PUT /app/rest/buildTypes/id:BackendServices_Build/parameters/env.NEXUS_URL
Content-Type: text/plain
https://nexus.yourcompany.com
5

Builds Endpoint

# List last 10 builds for a build config
GET /app/rest/builds?locator=buildType:BackendServices_Build,count:10

# Get a specific build
GET /app/rest/builds/id:12345
GET /app/rest/builds/buildType:BackendServices_Build,number:42

# Get last successful build on main branch
GET /app/rest/builds?locator=buildType:BackendServices_Build,branch:main,status:SUCCESS,count:1

# Get all running builds
GET /app/rest/builds?locator=running:true

# Get builds for a specific branch
GET /app/rest/builds?locator=buildType:BackendServices_Build,branch:(name:refs/heads/feature/login)

# Example response fields:
# id, number, status, state, branch, startDate, finishDate, duration,
# buildType, changes, artifacts, testOccurrences, revisions
6

Triggering a Build via REST

# Trigger a build (POST to buildQueue)
POST /app/rest/buildQueue
Content-Type: application/json
Accept: application/json

{
  "buildType": { "id": "BackendServices_Build" }
}

# Trigger on a specific branch
{
  "buildType": { "id": "BackendServices_Build" },
  "branchName": "refs/heads/feature/login"
}

# Trigger with custom parameters
{
  "buildType": { "id": "BackendServices_Deploy" },
  "branchName": "refs/heads/main",
  "properties": {
    "property": [
      { "name": "deploy.environment", "value": "staging" },
      { "name": "deploy.version",     "value": "1.2.42" }
    ]
  }
}

# Response: build object with id, number, status=queued
# Use the returned id to poll for completion:

# Poll build status
while true; do
  STATUS=$(curl -sH "Authorization: Bearer $TC_TOKEN" \
    "$TC_URL/app/rest/builds/id:$BUILD_ID" | jq -r '.status')
  STATE=$(curl -sH "Authorization: Bearer $TC_TOKEN" \
    "$TC_URL/app/rest/builds/id:$BUILD_ID" | jq -r '.state')
  echo "State: $STATE Status: $STATUS"
  [ "$STATE" = "finished" ] && break
  sleep 10
done
GOTCHA — buildQueue vs builds

POST to /app/rest/buildQueue to trigger a build. The build starts as "queued," then becomes "running," then "finished." GET from /app/rest/builds to query finished builds. GET from /app/rest/buildQueue to see builds still waiting for an agent. Confusing these is a common scripting mistake.

7

Pinning, Tagging & Commenting

# Pin a build (prevent cleanup from deleting it)
PUT /app/rest/builds/id:12345/pin
Content-Type: text/plain
Pinned for release 1.2.0

# Unpin
DELETE /app/rest/builds/id:12345/pin

# Add a tag to a build
POST /app/rest/builds/id:12345/tags
Content-Type: application/json
{ "tag": [{ "name": "release" }, { "name": "v1.2.0" }] }

# Add a build comment
PUT /app/rest/builds/id:12345/comment
Content-Type: text/plain
Deployed to production by ops team at 14:00 UTC
8

Downloading Artifacts

# List artifacts for a build
GET /app/rest/builds/id:12345/artifacts

# Download a specific artifact file
GET /app/rest/builds/id:12345/artifacts/content/path/to/myapp.jar
# Returns the file content directly (binary)

# Download last successful build's artifact
GET /app/rest/builds/buildType:BackendServices_Build,status:SUCCESS,branch:main,count:1/artifacts/content/artifacts/myapp.jar

# Download as file (curl)
curl -H "Authorization: Bearer $TC_TOKEN" \
  -o myapp.jar \
  "$TC_URL/app/rest/builds/buildType:BackendServices_Build,status:SUCCESS,count:1/artifacts/content/artifacts/myapp.jar"

# Alternative: direct download URL (no API version in path)
http://SERVER:8111/repository/download/BackendServices_Build/.lastSuccessful/artifacts/myapp.jar
9

Agents Endpoint

# List all agents
GET /app/rest/agents

# List unauthorized agents
GET /app/rest/agents?locator=authorized:false

# Authorize an agent
PUT /app/rest/agents/id:5/authorized
Content-Type: text/plain
true

# Disable an agent (takes it out of service, won't receive new builds)
PUT /app/rest/agents/id:5/enabled
Content-Type: text/plain
false

# Get agent details (including system properties reported)
GET /app/rest/agents/id:5
10

Test Results Endpoint

# Get test results for a specific build
GET /app/rest/testOccurrences?locator=build:12345

# Get only failed tests
GET /app/rest/testOccurrences?locator=build:12345,status:FAILURE

# Get muted tests
GET /app/rest/testOccurrences?locator=muted:true

# Get test history (all occurrences of a specific test)
GET /app/rest/testOccurrences?locator=test:name:com.example.UserServiceTest.testLogin

# Response fields: name, status, duration, details (stacktrace for failures)
11

Changes Endpoint

# Get changes (VCS commits) included in a build
GET /app/rest/changes?locator=build:12345

# Get changes for a build type since last successful build
GET /app/rest/changes?locator=buildType:BackendServices_Build,sinceChange:(buildType:BackendServices_Build,status:SUCCESS,count:1)

# Get a specific change
GET /app/rest/changes/id:678
# Returns: commit SHA, author, date, comment, files changed
12

Practical Scripts

Script 1: Trigger build and wait for result
#!/bin/bash
# trigger-and-wait.sh — triggers a build and waits for completion
set -euo pipefail

TC_URL="${1:-https://ci.yourcompany.com}"
BUILD_TYPE="${2:-BackendServices_Build}"
BRANCH="${3:-refs/heads/main}"
TC_TOKEN="${TC_TOKEN:?TC_TOKEN environment variable required}"

echo "Triggering $BUILD_TYPE on $BRANCH..."
RESPONSE=$(curl -sf -X POST \
  -H "Authorization: Bearer $TC_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  "$TC_URL/app/rest/buildQueue" \
  -d "{\"buildType\":{\"id\":\"$BUILD_TYPE\"},\"branchName\":\"$BRANCH\"}")

BUILD_ID=$(echo "$RESPONSE" | jq -r '.id')
echo "Build queued: ID=$BUILD_ID"

# Poll until finished
while true; do
  BUILD=$(curl -sf \
    -H "Authorization: Bearer $TC_TOKEN" \
    -H "Accept: application/json" \
    "$TC_URL/app/rest/builds/id:$BUILD_ID")
  STATE=$(echo "$BUILD" | jq -r '.state')
  STATUS=$(echo "$BUILD" | jq -r '.status')
  echo "  State: $STATE | Status: $STATUS"
  [ "$STATE" = "finished" ] && break
  sleep 15
done

echo "Build #$(echo "$BUILD" | jq -r '.number') finished: $STATUS"
[ "$STATUS" = "SUCCESS" ] && exit 0 || exit 1
Script 2: Download latest artifact
#!/bin/bash
# download-latest.sh — downloads the latest artifact from a build config
TC_URL="https://ci.yourcompany.com"
BUILD_TYPE="BackendServices_Build"
ARTIFACT_PATH="artifacts/myapp.jar"
OUTPUT="myapp.jar"

curl -sf \
  -H "Authorization: Bearer $TC_TOKEN" \
  -o "$OUTPUT" \
  "$TC_URL/app/rest/builds/buildType:${BUILD_TYPE},status:SUCCESS,branch:main,count:1/artifacts/content/${ARTIFACT_PATH}"

echo "Downloaded: $OUTPUT ($(du -sh "$OUTPUT" | cut -f1))"

Up Next — Phase 15: Advanced Topics

Full pipeline design patterns, matrix builds, conditional steps, clean-up policies, server backup, JVM performance tuning, Docker agents, and the upgrade procedure.

Continue to Phase 15 → Back to Hub