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

# Run an operation

> Call an approved operation from your application and handle what comes back.

Your SDK is installed and the client is up. This is the call itself — what you pass, what you get, and what can go wrong.

Services sit on the client in PascalCase. Operations either sit directly on the service or nest under a resource, following the provider's own grouping — your package's `README.md` lists the exact paths for the operations you selected.

Python puts both clients on the same object: `sdk.Linear` is synchronous, `sdk.AsyncLinear` is not. Resources and methods are snake\_case.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await sdk.Linear.issueUpdate({ id: 'ISS-42', state: 'done' });

  if (result.ok) {
    console.log(result.data);
  } else {
    console.error(result.status, result.error);
  }
  ```

  ```python Python theme={null}
  result = await sdk.AsyncLinear.issue_update({"id": "ISS-42", "state": "done"})

  if result["ok"]:
      print(result["data"])
  else:
      print(result["status"], result["error"])
  ```
</CodeGroup>

<Warning>
  Every call returns an envelope — `{ ok, status, data, error }` — never the raw payload. Check `ok` before touching `data`: if `ok` is `false`, the provider still responded, just with an error, so `data` will be `null` and `error` holds what they actually said.
</Warning>

## Two kinds of failure

`ok: false` means the provider replied and said no. Anything that stopped the call reaching that point is **thrown** instead, already typed:

| Thrown                   | Means                                              | Carries                                                 |
| ------------------------ | -------------------------------------------------- | ------------------------------------------------------- |
| `AuthError`              | The credential the Engine holds was rejected       | `integration`, `endpoint`                               |
| `RateLimitError`         | The provider throttled you past Engine retry       | `retryAfterMs`                                          |
| `ExecutionTimeoutError`  | Your deadline or the Engine's policy expired first | `timeoutMs`, `code`                                     |
| `ReconnectRequiredError` | A connected user's grant needs re-consent          | `endUserRef`, `connectionId`, `reason`                  |
| `IntegrationError`       | Base class for the above                           | `integration`, `endpoint`, `statusCode`, `responseBody` |

So a real call site branches on `ok` and catches around it:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { ReconnectRequiredError, RateLimitError } from 'support-sdk';

  try {
    const result = await sdk.Linear.issueUpdate({ id: 'ISS-42', state: 'done' });
    if (!result.ok) return handleProviderRejection(result.error);
    return result.data;
  } catch (err) {
    if (err instanceof ReconnectRequiredError) return promptReconnect(err.endUserRef);
    if (err instanceof RateLimitError) return retryAfter(err.retryAfterMs);
    throw err;
  }
  ```

  ```python Python theme={null}
  from fused.support_sdk import ReconnectRequiredError, RateLimitError

  try:
      result = await sdk.AsyncLinear.issue_update({"id": "ISS-42", "state": "done"})
      if not result["ok"]:
          return handle_provider_rejection(result["error"])
      return result["data"]
  except ReconnectRequiredError as err:
      return prompt_reconnect(err.end_user_ref)
  except RateLimitError as err:
      return retry_after(err.retry_after_ms)
  ```
</CodeGroup>

<Note>
  `ReconnectRequiredError` is the one worth handling deliberately. It means a specific end user's consent lapsed — not that your SDK token is bad. Start a replacement with [`sdk.auth.startConnectSession`](/bucket/connect-user-accounts), and do not infer it from a bare 401.
</Note>

## Per-call options

Anything about *this* call rather than the SDK goes in a `fused` block: which end user to act as, which auth scheme to pick when a service declares several, and a page ceiling.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await sdk.Linear.issueList({
    pageSize: 100,
    fused: {
      endUserRef: 'customer-123',
      pagination: { maxPages: 5 },
    },
  });
  ```

  ```python Python theme={null}
  await sdk.AsyncLinear.issue_list(
      {"pageSize": 100},
      options={"fused": {
          "end_user_ref": "customer-123",
          "pagination": {"max_pages": 5},
      }},
  )
  ```
</CodeGroup>

| Option     | TypeScript / Python                            | What it does                                                   |
| ---------- | ---------------------------------------------- | -------------------------------------------------------------- |
| End user   | `endUserRef` / `end_user_ref`                  | Routes OAuth or OIDC through a connected user's grant          |
| Auth type  | `authType` / `auth_type`                       | Picks `oauth`, `oidc`, `basic`, `bearer`, `api_key`, or `mtls` |
| Auth name  | `authName` / `auth_name`                       | Disambiguates several declared schemes of one type             |
| Resource   | `resourceId` / `resource_id`                   | Chooses an already connected tenant or account                 |
| Pagination | `pagination.maxPages` / `pagination.max_pages` | Tightens the Engine's page ceiling for this call               |

`maxPages` must sit strictly below the effective service policy — equality is rejected. You still get one aggregated result from one execution, so do not build a second page loop around it.

## Streaming operations

An operation the provider streams comes back as an async iterable on `data`, inside the same envelope.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const stream = await sdk.Acme.eventsWatch({ since: cursor });

  if (stream.ok) {
    for await (const event of stream.data) {
      handle(event);
    }
  }
  ```

  ```python Python theme={null}
  stream = await sdk.AsyncAcme.events_watch({"since": cursor})

  if stream["ok"]:
      async for event in stream["data"]:
          handle(event)
  ```
</CodeGroup>

Stopping iteration cancels the underlying call. Bound long streams with `streamIdleTimeoutMs` and `maxStreamDurationMs` on the client.

## Read the receipts

Every execution leaves a canonical receipt, whichever route made it.

```bash theme={null}
fused-cli sdk activity support-sdk@1.1.0
fused-cli sdk activity support-sdk@1.1.0 --all-versions --status failed
```

The JSON page carries receipt and trace IDs, provider status, total and provider-side latency, attempt counts, and failure classification. Narrow it with `--status`, `--start`, `--end`, `--limit`, and `--offset`.

`sdk activity` needs both `app.read` and `audit.read`. Use it rather than querying Engine GraphQL directly.

## Calling it another way

<CardGroup cols={2}>
  <Card title="Test from the CLI" icon="terminal" href="/app/testing/from-the-cli">
    `sdk invoke` for a smoke test or a CI gate — not for application code.
  </Card>

  <Card title="Call over REST" icon="cloud" href="/app/testing/over-rest">
    One HTTP endpoint, for languages with no generated package.
  </Card>
</CardGroup>

<Card title="Call a unified operation" icon="sitemap" href="/app/unified/call">
  One call across several services, with targets and per-service selectors.
</Card>
