---
id: operations
title: 'Invoking Operations'
description: 'Execute Agility operations against one asset or every asset matching a query, with args, list, and subquery parameter forms.'
sidebar_label: 'Invoking Operations'
sidebar_position: 7
tags: ['api', 'bulk', 'asset', 'operations']
---

An execute command is a command object with a `from` clause and an `execute`
clause. Assets it acts on are reported in `assetsOperatedOn`.

## The Bare Form

When an operation takes no parameters, `execute` is just the operation name.

```json title="Single asset"
{ "from": "Task:1592", "execute": "Delete" }
```

`filter` (the full expression language) and `with` variables are accepted on
an execute command exactly as on an update — `where` below is just the
shortest form.

```json title="Every asset matching a query"
{
  "from": "Workitem",
  "where": { "Parent": "Story:1591" },
  "execute": "Delete"
}
```

```json title="Response"
{
  "assetsOperatedOn": { "oidTokens": ["Task:1592", "Test:1593"], "count": 2 }
}
```


Regardless of which form you use, the projection phase normalises `execute`
into an operation name plus an argument bag:

```json
{ "oid": "Scope:0", "execute": { "op": "NonexistentOperation", "args": {} } }
```


## The `args` Form — One Invocation with Parameters

Use an object with `op` and `args` when the operation takes parameters.

```yaml
from: Member:1040
execute:
 op: AssignToScopeWithRole
 args:
  Scope: Scope:1503
  Role: Role:3
  IsOwner: true
```

```json
{
  "from": "Member:1040",
  "execute": {
    "op": "AssignToScopeWithRole",
    "args": { "Scope": "Scope:1503", "Role": "Role:3", "IsOwner": true }
  }
}
```

The response reports one entry in `assetsOperatedOn`.


## The `list` Form — Repeated Invocation

`list` replaces `args` with an array of argument bags. The operation is invoked
once per entry, against the same `from` target.

```yaml
from: Member:1040
execute:
 op: AssignToScopeWithRole
 list:
 - Scope: Scope:1503
   Role: Role:3
   IsOwner: true
 - Scope: Scope:1504
   Role: Role:7
   IsOwner: false
```

```json
{
  "from": "Scope:1503",
  "execute": {
    "op": "AssignMemberWithRole",
    "list": [
      { "Member": "Member:1040", "Role": "Role:3", "IsOwner": true },
      { "Member": "Member:1041", "Role": "Role:7", "IsOwner": false }
    ]
  }
}
```

The **same OID token appears once per invocation** in
`assetsOperatedOn.oidTokens` — a two-entry `list` against one asset yields
`count: 2` with the token repeated.


## Subquery Arguments

Any value inside `args` may itself be a query. This lets a script address
assets by a stable business value instead of an instance-specific OID token.

```yaml
from: Member:1040
execute:
 op: AssignToScopeWithRole
 args:
  Scope:
    from: Scope
    where:
     Name: Subquery project for Member.AssignToScopeWithRole
  Role:
    from: Role
    where:
     Name: Role.Name'Project Lead
  IsOwner: true
```

```json
{
  "from": "Scope:1503",
  "execute": {
    "op": "AssignMemberWithRole",
    "args": {
      "Member": { "from": "Member", "where": { "Username": "scopeMember2" } },
      "Role": { "from": "Role", "where": { "Name": "Role.Name'Project Lead" } },
      "IsOwner": true
    }
  }
}
```

:::warning Only the first match is used
When a subquery supplies an operation argument, only the very first result
becomes the value. If the subquery could match more than one asset, add a
`sort` clause to make the choice deterministic.
:::


This differs from a subquery used in a
[relation update](/apis/asset-bulk/relation-updates#referencing-by-subquery),
where **every** match is used.

## Undelete

Deleted assets are excluded from queries by default, so `Undelete` must
explicitly opt in with `IsDeleted: true`.

```yaml
from: Story:1591
where:
 IsDeleted: true
execute: Undelete
```


## Deferring an Operation Until Processing

An `execute` command that targets an asset created earlier in the same request
must be wrapped in a user alias, so its projection is deferred until the
processing phase — by which point the alias resolves to a real OID token.

```json
[
  {
    "@story": {
      "AssetType": "Story", "Scope": "Scope:0", "Name": "Story Test",
      "Children": [
        { "@test1": { "AssetType": "Test", "Name": "My Test 1" } },
        { "AssetType": "Test", "Name": "My Test 2" }
      ]
    }
  },
  { "@inactivate": { "from": "@test1", "execute": "Inactivate" } },
  {
    "from": "Test",
    "where": { "Parent": "@story" },
    "select": ["Name", "AssetState"]
  }
]
```

See
[Deferring commands](/apis/asset-bulk/aliases-and-projection#deferring-commands-until-the-processing-phase)
for the full explanation.

## Unknown Operations

Naming an operation the asset type does not support produces a command failure
with HTTP `200`:

```json
{
  "commands": [
    {
      "@op": { "oid": "Scope:0", "execute": { "op": "NonexistentOperation", "args": {} } },
      "error": {
        "message": "Unknown Operation 'Scope.NonexistentOperation'",
        "sourceCommandIndex": 0
      }
    }
  ],
  "count": 1
}
```


## Related Topics

- [Operations](/concepts/operations)
- [Projection, processing and aliases](/apis/asset-bulk/aliases-and-projection)
- [Errors and partial failures](/apis/asset-bulk/errors)
