> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnicommerce.sg/llms.txt
> Use this file to discover all available pages before exploring further.

# AOP API

> Create, configure, execute, retry, and poll Agent Operating Procedures over REST.

Use the developer AOP API to run saved Agent Operating Procedures from an external system.

Interactive playground: [API reference](/api-reference) · Spec: [openapi.json](/openapi.json)

## Authentication

```http theme={null}
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json
```

| Endpoint                                 | Required scope |
| ---------------------------------------- | -------------- |
| `POST /api/v1/aop/{aopId}/execute`       | `aops:write`   |
| `POST /api/v1/aop/retry`                 | `aops:write`   |
| `POST /api/v1/aop/create`                | `aops:write`   |
| `PUT /api/v1/aop/{aopId}/config`         | `aops:write`   |
| `GET /api/v1/aop/{aopId}/config`         | `aops:read`    |
| `GET /api/v1/threads/{thread_id}/status` | `aops:read`    |

<Note>
  API keys created from organization settings receive all developer scopes by
  default. Existing keys created before this API shipped need `aops:read` /
  `aops:write` added in organization settings.
</Note>

OAuth clients must include the scope on the client. Session-authenticated calls must include `organizationId` in the query (GET) or JSON body (writes). Do **not** send `organizationId` for API-key requests.

`aopId` in the path (and `aop_id` in JSON responses) is the OmniCommerce AOP id from `/app/{organizationId}/aops`.

## Execute an AOP

```http theme={null}
POST /api/v1/aop/{aopId}/execute
```

<Steps>
  <Step title="Start the run">
    The AOP id is in the path. Optional `user_inputs` go in the JSON body. The response returns immediately with a `thread_id`.

    ```bash theme={null}
    curl -X POST "https://omnicommerce.sg/api/v1/aop/aop_1/execute" \
      -H "Authorization: Bearer $OMNI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "user_inputs": {
          "company": "Acme Corp",
          "quarter": "Q1 2024"
        }
      }'
    ```
  </Step>

  <Step title="Poll until terminal">
    ```bash theme={null}
    curl "https://omnicommerce.sg/api/v1/threads/THREAD_ID/status" \
      -H "Authorization: Bearer $OMNI_API_KEY"
    ```

    Status values: `running`, `completed`, `failed`. Terminal responses include conversation messages by default. Pass `include_messages=true` to force messages while the run is still in progress.
  </Step>
</Steps>

### Request body

| Field            | Required | Description                                                                                            |
| ---------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `user_inputs`    | No       | Key/value inputs appended as a `--- User Inputs ---` block and bound to `[[ placeholder ]]` variables. |
| `dry_run`        | No       | When `true`, run as a Test run: read-only tools execute, side-effectful tools are captured.            |
| `organizationId` | Session  | Required for session auth.                                                                             |

### Response

```json theme={null}
{
  "status": "started",
  "thread_id": "agent-aop-run_1",
  "trigger_type": "api",
  "aop_id": "aop_1",
  "aop_title": "Market Research Report Generator",
  "base_prompt": "Generate a comprehensive market research report",
  "final_prompt": "Generate a comprehensive market research report\n\n--- User Inputs ---\ncompany: Acme Corp\nquarter: Q1 2024\n",
  "aop_config": { "agentId": "research" },
  "sync_server": "https://omnicommerce.sg",
  "message": "Task execution started successfully. Use the thread_id to track progress."
}
```

`trigger_type` is always `api` for this endpoint. `sync_server` is the OmniCommerce origin and can be ignored.

## Poll status

```http theme={null}
GET /api/v1/threads/{thread_id}/status
```

```bash theme={null}
curl "https://omnicommerce.sg/api/v1/threads/agent-aop-run_1/status?include_messages=true" \
  -H "Authorization: Bearer $OMNI_API_KEY"
```

| Query              | Description                                                                                           |
| ------------------ | ----------------------------------------------------------------------------------------------------- |
| `include_messages` | `true` to include messages while the run is still in progress. Terminal runs include them by default. |
| `organizationId`   | Session auth only.                                                                                    |

`conversation_asset` includes `linked_aops`, `last_message`, and `metadata.run_status` (including `awaiting_approval`).

## Retry a failed run

```http theme={null}
POST /api/v1/aop/retry
```

```bash theme={null}
curl -X POST "https://omnicommerce.sg/api/v1/aop/retry" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "thread_id": "agent-aop-run_1" }'
```

Only failed AOP runs can be retried. Optional `user_inputs` replace the original values; omit them to reuse the failed run's inputs. Response:

```json theme={null}
{
  "status": "started",
  "new_thread_id": "agent-aop-run_2",
  "message": "Retry execution started successfully."
}
```

## Create an AOP

```http theme={null}
POST /api/v1/aop/create
```

```bash theme={null}
curl -X POST "https://omnicommerce.sg/api/v1/aop/create" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Market Research Report",
    "prompt": "Generate a comprehensive market research report for [[ company ]]",
    "agentId": "research"
  }'
```

Use `[[ placeholder ]]` in the prompt for execution-time inputs. Omni validates a name of at least 2 characters and a prompt of at least 10 characters.

| Field                       | Description                                                           |
| --------------------------- | --------------------------------------------------------------------- |
| `title`                     | AOP name. Defaults to `Untitled AOP`.                                 |
| `prompt`                    | Instructions. `[[ company ]]` becomes a `user_inputs.company` field.  |
| `agentId`                   | Assigned agent template id. Defaults to OmniBot.                      |
| `structured_output`         | JSON Schema for structured results.                                   |
| `agent_config`              | Optional `chatModel` / `planModeEnabled`. Does not grant extra tools. |
| `parent_folder_id`          | Optional workstream id.                                               |
| `user_notification_configs` | Per-user outcome notification map.                                    |
| `organizationId`            | Session auth only.                                                    |

Response includes `aop_id`, `title`, `status: "created"`, and `parent_folder_id`.

## Read and overwrite config

```http theme={null}
GET /api/v1/aop/{aopId}/config
PUT /api/v1/aop/{aopId}/config
```

`GET` returns prompt, `agentId`, `structured_inputs` (computed from placeholders), `structured_output`, and notification settings.

`PUT` updates provided fields only. Omitted fields keep their current values. Send `user_notification_configs: null` to clear notification overrides.

```bash theme={null}
curl "https://omnicommerce.sg/api/v1/aop/aop_1/config" \
  -H "Authorization: Bearer $OMNI_API_KEY"

curl -X PUT "https://omnicommerce.sg/api/v1/aop/aop_1/config" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Generate a weekly pricing brief for [[ marketplace ]]",
    "agentId": "pricing"
  }'
```

## Errors

| Status | Meaning                                                                                              |
| ------ | ---------------------------------------------------------------------------------------------------- |
| `400`  | Invalid JSON, missing `thread_id`, failed-run retry on a non-failed thread, or AOP draft validation. |
| `401`  | Missing or invalid bearer token.                                                                     |
| `402`  | Organization is out of AI credits.                                                                   |
| `403`  | Token is missing `aops:read` or `aops:write`.                                                        |
| `404`  | AOP or thread was not found in the authenticated organization.                                       |
| `429`  | Rate limit. Retry later.                                                                             |

## Field notes

| Field              | Meaning                                          |
| ------------------ | ------------------------------------------------ |
| `aop_id`           | Organization AOP id                              |
| `organizationId`   | Session auth organization id                     |
| `user_inputs`      | AOP variable values                              |
| `parent_folder_id` | Assigned workstream id                           |
| `dry_run`          | Test run (`simulation: { "mode": "read_only" }`) |
