Skip to main content

cURL Examples

The examples below form one sequence — the same operations appear in cURL, Python and TypeScript — so you can switch languages without switching mental models. Set up authentication first.

Setup​

export AGILITY_BASE_URL="https://www7.v1host.com/YourInstance"
export AGILITY_ACCESS_TOKEN="your-token-here"

Who Am I?​

The best connection test — it proves the URL and the credentials, and tells you which member the token acts as:

curl "${AGILITY_BASE_URL}/query.v1" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"from": "Member", "where": {"IsSelf": true}, "select": ["Name", "Nickname", "Email"]}'
The response envelope

query.v1 returns an array of result arrays — one inner array per query in the request. There is no results or query wrapper object:

[
[
{ "_oid": "Story:1207", "Number": "S-01001", "Name": "Logon" }
]
]

Get One Story by Number​

curl "${AGILITY_BASE_URL}/query.v1" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"from": "Story",
"where": { "Number": "S-01001" },
"select": ["Number", "Name", "Description", "Status.Name"]
}'

List a Backlog, Paged Correctly​

Page until a response comes back shorter than the page size — no total needed. Scope.ParentMeAndUp means "in this project or any project beneath it":

SIZE=50; START=0
while :; do
COUNT=$(curl -s "${AGILITY_BASE_URL}/query.v1" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"from\": \"PrimaryWorkitem\",
\"filter\": [\"Scope.ParentMeAndUp='Scope:0'\"],
\"select\": [\"Number\", \"Name\", \"Status.Name\"],
\"sort\": [\"Number\"],
\"page\": { \"size\": ${SIZE}, \"start\": ${START} }
}" | jq '.[0] | length')
echo "fetched ${COUNT} at offset ${START}"
[ "${COUNT}" -lt "${SIZE}" ] && break
START=$((START + SIZE))
done

Filter Precisely​

Quote filter values, compare relations by OID, and prefer RollupState and UTC dates for queries that survive renames and timezones:

curl "${AGILITY_BASE_URL}/query.v1" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"from": "Story",
"where": { "Scope": "Scope:0" },
"filter": ["Estimate>'"'"'2'"'"'", "Status.RollupState='"'"'Active'"'"'"],
"select": ["Number", "Name", "Estimate", "Status.Name"],
"sort": ["-ChangeDateUTC"],
"page": { "size": 50, "start": 0 }
}'

Traverse Relations​

Dotted paths flatten into their own keys; a nested subquery groups child attributes into objects:

curl "${AGILITY_BASE_URL}/query.v1" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"from": "Story",
"where": { "Number": "S-01001" },
"select": [
"Name", "Scope.Name", "Owners.Name",
{ "from": "Children:Task", "select": ["Number", "Name", "Status.Name"] }
]
}'

Create and Update with the Bulk API​

One surface for writes; the response lists what was created, modified and what failed:

# create
curl "${AGILITY_BASE_URL}/api/asset" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '[{ "AssetType": "Story", "Name": "My new story", "Scope": "Scope:0" }]'

# update the story it returned (see assetsCreated.oidTokens)
curl "${AGILITY_BASE_URL}/api/asset" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '[{ "from": "Story:3884", "update": { "Estimate": 3, "Owners": { "add": ["Member:20"] } } }]'

Several commands can share one request, and later commands can reference earlier ones by alias:

curl "${AGILITY_BASE_URL}/api/asset" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '[
{ "@epic": { "AssetType": "Epic", "Name": "New initiative", "Scope": "Scope:0" } },
{ "@story": { "AssetType": "Story", "Name": "First slice", "Scope": "Scope:0", "Super": "@epic" } }
]'

Run an Operation​

Lifecycle changes are operations on rest-1.v1 — query parameter, empty body:

curl -X POST "${AGILITY_BASE_URL}/rest-1.v1/Data/Story/3884?op=Inactivate" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}"

Handle Failures Properly​

A Bulk API request answers 200 even when commands fail — always check commandFailures, and resolve violation tags to human-readable text through the localizer (unmatched tags echo back unchanged):

RESPONSE=$(curl -s "${AGILITY_BASE_URL}/api/asset" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '[{ "from": "Story:3884", "update": { "Name": "renamed" } }]')

FAILS=$(echo "${RESPONSE}" | jq '.commandFailures.count')
if [ "${FAILS}" -gt 0 ]; then
TAG=$(echo "${RESPONSE}" | jq -r '.commandFailures.commands[0].error.message')
curl -s "${AGILITY_BASE_URL}/loc-2.v1?[${TAG}]" \
-H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}"
fi