---
id: curl
title: 'cURL Examples'
description: 'Runnable cURL examples for the Digital.ai Agility API'
sidebar_label: 'cURL'
sidebar_position: 1
tags: ['examples', 'curl']
---

The examples below form one sequence — the same operations appear in
[cURL](/examples/curl), [Python](/examples/python) and
[TypeScript](/examples/typescript) — so you can switch languages without
switching mental models. Set up
[authentication](/getting-started/authentication) first.

## Setup

```bash
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:

```bash
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"]}'
```

:::note 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:

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

:::

## Get One Story by Number

```bash
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":

```bash
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:

```bash
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:

```bash
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:

```bash
# 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:

```bash
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:

```bash
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):

```bash
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](/guides/query-size-and-performance) — paging and projection discipline
- [Bulk API](/apis/asset-bulk) — the full command reference
- [Error conventions](/apis/conventions/errors) — including localizable violation tags
