Skip to main content

Agent Primer

This page briefs an AI agent on calling the Agility API. It is written for models, not people: dense, imperative, and exhaustive about failure modes. Every rule links the page that owns the full explanation — when this page and an owning page disagree, the owning page wins.

Choosing a Surface​

TaskUse
Read one asset, or write attributesrest-1.v1/Data/<Type> — rest-1
Read with shaped/nested JSON projectionsquery.v1 (read-only) — query.v1
Create/update/execute in bulk, aliasesapi/asset — Bulk API
Discover types, attributes, operationsmeta.v1 (no auth) — meta.v1
Resolve display names, localize tagsloc-2.v1 (no auth) — loc-2.v1
Instance settings: attachment size limit, time zone, build versionconfig.v1 (no auth, XML only) — config.v1
Read past values / change historyrest-1.v1/Hist/<Type> and asof — moments
Read member-scope role assignmentsapi/ProjectRoles — guide
Link a human to an assetassetdetail.v1?oid=… or ?Number=… — permalinks; never emit these for programs

Non-Negotiable Query Rules​

  • Every list query carries an explicit selection and page. Omitting them returns every default attribute of every matching asset. rest-1.v1: sel=Name,Number&page=50,0 (positional, size first). query.v1: "select": [...] and "page": {"size": 50, "start": 0} (named, both keys required — omitting start is a server error). Never port the positional form into query.v1 or vice versa. Owner.
  • Do not alias. "Name as Title" and "from": "Story as s" parse and are silently discarded — response keys are always the attribute tokens. Owner.
  • A paged response reports no total unless you ask: rest-1.v1 omits the total attribute, api/asset reports null; send needTotal=true when paging off the count, and treat its absence as "unknown", not zero. query.v1 never returns a total at all. Owner.
  • Never invent attribute names. Fetch meta.v1/<Type> and use the tokens it returns. Custom fields (Custom_*) vary per instance. Owner.
  • Resolve a UI word to a system name in this order: the display-names table → its informal-names and false-friends sections → assume an instance terminology override and enumerate via meta.v1 + loc-2.v1 (procedure). Beware: "Planning Level" usually means Scope, not PlanningLevel; "Message" is an inbox notification, not a comment.
  • query.v1 responses are double-wrapped — one array per query in the request document: [[...results...]]. Dotted select tokens come back as flattened keys ("Scope.Name"); bare relation selects return {"_oid": "..."} objects. Owner.
  • Fetch by OID with "where": {"ID": "Story:1207"} (query.v1) or the URL path (rest-1). Match on OIDs, never on display names — display names are localized, instance-configurable, and not unique.

Non-Negotiable Write Rules​

  • rest-1.v1 JSON writes wrap every attribute: {"Attributes": {"Name": {"value": "...", "act": "set"}}}. Multi-value relations use "act": "add" / "remove". Owner.
  • api/asset documents are named commands, one aliased command per array element: [{"@alias": {"AssetType": "Story", ...}}, {"@next": {"from": "@alias", "update": {...}}}]. Aliases resolve in order. Never put two @ keys in one object: two siblings happen to work, three or more fail with Invalid key at node(s) and nothing is created. Owner.
  • Operations execute as "execute": "Delete" (no arguments) or "execute": {"op": "AssignToScopeWithRole", "args": {...}} (parameterized). A with block does not pass operation arguments. Owner.
  • Never write a Rank attribute (Order, AttributeType Rank) with a numeric set — the server rejects it (Violation'NotSupported). Rank moves are relative, and rank changes leave no history.
  • Closed assets accept backfill only: writing an attribute that already has a value fails with Violation'ClosedAssetCannotEditExistingValues. Owner.
  • Delete is soft and recoverable (Undelete); Inactivate closes (AssetState Closed). A closed asset cannot be deleted (Violation'AssetCannotDeleteClosed). Owner.
  • Status writes must be in the team's allowed set when the team enforces a process (Violation'PrimaryWorkitemCannotHaveInvalidStatus). Ask api/ValidValues before writing, or inspect Team.HasProcess / Team.SelectedValues. Owner.
  • A 200 from an operation means accepted, not transitioned. Some redundant operations no-op with a new history moment (Reactivate on an Active asset); others fail a rule. Read the asset's state back when the transition matters. Owner.

Error Handling​

  • Violations return structured tags (Violation'...). The tags are localizable through loc-2.v1; unresolved tags echo back unchanged, so blanket resolution is always safe. Owner.
  • An unparseable api/asset document returns an opaque 500, not a 400 — do not retry unchanged; fix the document shape.
  • A 400 Wrong OID AssetType means the relation's declared type rejects the target. Check the attribute's RelatedAsset type in meta.v1 — and do not generalize between sibling types (Task.Parent and Test.Parent are typed differently). Owner.

Common Recipes​

  • Comment on a workitem (with a mention): create a Conversation, an Expression with BelongsTo (or reply with InReplyTo), and anchor via Mentions. One-request payload: working with comments.
  • Scope-tree filtering: Scope.ParentMeAndUp[Schedule=$S;ScopeLabels=$L]=$Scope with with variables; arrays in with are in-list matches. For large trees, precompute the scope set first. Owner.
  • Who did what, when: Hist queries with History[...] projections; DoneDate is stamped when EffectiveAssetState reaches Closed. Not every attribute is tracked. Owner.
  • Grant access: default role via Member.Scopes add; a specific role via the AssignToScopeWithRole / AssignMemberWithRole operations; read back via api/ProjectRoles, never via asset queries. Owner.

Authentication​

Access tokens never expire and carry the member's full rights — send Authorization: Bearer <token>. Basic auth is a dev/demo configuration only; production instances almost always use SSO. OAuth2 has never been supported despite legacy naming. Owner.