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"]}'
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
Related Topics
- Query size and performance — paging and projection discipline
- Bulk API — the full command reference
- Error conventions — including localizable violation tags