---
title: "Deferred tool results"
description: "A tool backend may defer the result of the current tool call without creating a user message or asking the model to call it again. Core understands only an operation id and opaq…"
url: "https://sotaagents.ai/manual/developer-guide/deferred-tool-results"
generated_by: "sotaagents-ldp"
docs_index: "https://sotaagents.ai/manual/llms.txt"
locale: "en"
---

# Deferred tool results

A tool backend may defer the result of the current tool call without creating a user message or asking the model to call it again. Core understands only an operation id and opaque app data; login, approval, payment, device pairing, or any other workflow remains app logic.

JSON

```json
{
  "_sota": {
    "deferredToolResult": {
      "operationId": "app-defined-globally-unique-id",
      "data": { "anyAppOwnedValue": true }
    }
  }
}
```

Core registers the exact run, tool call, tool name, app installation, and environment behind that id. It streams `output-pending` to matching tool surfaces and adds the same item to the exact composer panel's `pendingToolResults`. The model has not received a tool result yet.

YAML

```yaml
coreToolGrants:
  - tool: core.app-operations.complete
    scope: write
  # Only when a longer-lived job callback token is needed:
  - tool: core.tokens.issueJobCallback
    scope: write
```

Send the callback from the app backend to the Core origin. The bearer value is the delegated capability received in the original invocation's `x-sota-core-token` header.

Code

```
POST /v1/app-operations/app-defined-globally-unique-id/complete
Authorization: Bearer <delegated-token>
Content-Type: application/json

{
  "status": "completed",
  "result": { "events": [] }
}
```

Use `status: "failed"` with an arbitrary `error` to reject it. The completion atomically becomes the result of the original tool call, and the same agent run continues. Parallel tool calls each have their own operation id; the model step continues only after all calls in that step settle.

JSON

```json
{
  "status": "failed",
  "error": {
    "code": "ACTION_NOT_COMPLETED",
    "message": "The requested action was not completed"
  }
}
```

- Grant `core.app-operations.complete:write` to the app backend.
- Operation ids are trimmed, 1–200 characters, and globally unique per tool call. Identical completion retries are idempotent.
- Registration occurs after Core receives the deferred response, so retry `app_operation_not_found` with bounded backoff.
- If completion can outlive the 60-second delegation token, exchange it for a job callback token while the original invocation is still valid, then store that token safely and send its job id in `x-sota-job-id`.
- Keep `data` small and free of secrets: it is opaque to Core but deliberately client-visible. Narrow its shape in app UI before use.
- The current primitive stays attached to the live run. Stopping or steering the run fails a still-pending operation with `operation_cancelled`. App-owned expiry should complete the operation as failed; this is not a background job queue.
