---
title: "useAppFetch"
description: "useAppFetch() returns the mounted surface's authenticated data-plane fetcher. Pass an app-relative path and ordinary RequestInit; Core resolves it under the selected environment…"
url: "https://sotaagents.ai/manual/developer-guide/use-app-fetch"
generated_by: "sotaagents-ldp"
docs_index: "https://sotaagents.ai/manual/llms.txt"
locale: "en"
---

# useAppFetch

`useAppFetch()` returns the mounted surface's authenticated data-plane fetcher. Pass an app-relative path and ordinary `RequestInit`; Core resolves it under the selected environment's backend, injects the bearer credential, binds cancellation to the mount, and refreshes an expired credential once.

TSX

```
import { useState } from 'react';
import { useAppFetch } from '@sota/platform';

export function SaveSettingsButton() {
  const appFetch = useAppFetch();
  const [status, setStatus] = useState('idle');

  async function save() {
    setStatus('saving');
    const response = await appFetch('/settings', {
      method: 'PUT',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ digest: 'weekly' }),
    });
    if (!response.ok) {
      setStatus('error');
      return;
    }
    const saved = await response.json();
    setStatus(saved.digest === 'weekly' ? 'saved' : 'error');
  }

  return <button type="button" onClick={save}>{status}</button>;
}
```

The returned value is a standard `Response`; JSON parsing and domain errors remain app logic. Absolute cross-origin URLs and paths escaping the scoped base path are rejected. Do not add or persist an authorization header yourself.

| Case | Behavior |
| --- | --- |
| Normal response | Returned unchanged, including non-2xx status codes. |
| Expired app-data credential | Core refreshes the descriptor and retries once in the same environment. |
| Surface unmount | The request signal is aborted. |
| One-shot stream body | Core tees it before a possible credential retry. |
