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

# useVaApi

> Singleton axios instance for the bound airline's API with auto-refresh on 401.

`useVaApi` returns a shared [axios](https://axios-http.com/) instance that's pre-configured to talk to the bound airline's API. The shell takes care of attaching the pilot's bearer token, swapping the base URL when the user switches airlines, and refreshing the access token on `401` responses — your plugin just makes requests.

The instance is provided by `PluginShellProvider`, which means **every plugin and the shell itself share the same axios singleton in memory**. Concurrent 401s across plugins coalesce onto a single in-flight refresh — only one `POST /api/auth/va/refresh` is sent regardless of how many plugins are racing.

## Import

```ts theme={null}
import { useVaApi } from "@skyvexsoftware/stratos-sdk";
```

## Signature

```ts theme={null}
function useVaApi(): AxiosInstance;
```

The returned instance is the same singleton on every render — safe to capture in `useMemo` or pass into TanStack Query without worrying about churn.

## What you get for free

* **`baseURL`** is set to the airline's `base_url` from the Skyvex platform (the same value as `usePluginContext().airline?.base_url`). Resolved per-request, so plugins don't need to react to airline changes.
* **`Authorization`** header is set to the current VA bearer.
* **Refresh on 401**: if the VA exposes a `refresh_url` and the user has a refresh token, the shell exchanges the refresh token for a new access token and the request is retried exactly once. If refresh isn't supported or the VA rejects it, the `401` propagates and the shell's existing re-auth flow takes over.

There's no manual refresh trigger — refresh fires only when the airline returns `401`.

## Usage

```tsx theme={null}
import { useVaApi } from "@skyvexsoftware/stratos-sdk";
import { useQuery } from "@tanstack/react-query";

type PilotProfile = {
  pilotID: string;
  rank: string;
  hours: number;
};

export function PilotCard() {
  const va = useVaApi();

  const { data, isLoading, error } = useQuery({
    queryKey: ["pilot-profile"],
    queryFn: async () => {
      const res = await va.get<PilotProfile>("/pilot/verify");
      return res.data;
    },
  });

  if (isLoading) return <p>Loading…</p>;
  if (error) return <p>Failed to load profile</p>;
  return <p>{data?.pilotID} — {data?.rank}</p>;
}
```

### POST / PUT / DELETE

```tsx theme={null}
const va = useVaApi();

await va.post("/pireps", {
  flight_number: "QFA1",
  arrival: "EGLL",
});
```

### Inspecting status codes

The interceptor only retries `401`. Other non-2xx responses behave like a normal axios call — the promise rejects with an `AxiosError` whose `response.status` you can branch on:

```tsx theme={null}
try {
  await va.get("/some/endpoint");
} catch (err) {
  if (axios.isAxiosError(err) && err.response?.status === 404) {
    // not found
  }
}
```

## When to reach for it vs. raw fetch

| Case                                                               | Use `useVaApi()` | Use raw `fetch` / `auth.token`     |
| ------------------------------------------------------------------ | ---------------- | ---------------------------------- |
| Calling the VA's API with the pilot's bearer                       | ✅                | ❌ — you'd reimplement refresh      |
| Calling a third-party API that doesn't share the VA's auth         | ❌                | ✅                                  |
| Calling your plugin's own background routes (`/api/my-plugin/...`) | ❌                | ✅ — those don't need the VA bearer |

For background-module callers, see [`ctx.airline.createClient()`](/sdk/background/context/airline) — same behaviour, different process.

## Notes

* The hook reads the shared axios instance from `PluginShellProvider`. Calling it outside that provider (e.g. in a unit test that doesn't render the shell wrapper) will throw — mock the provider in tests or import the standalone `vaApi` export from the SDK if you really need to call it bare.
* The token is read from the shell on every request, so plugins always send the latest bearer (including immediately after a refresh). No `useEffect` plumbing required.
* This hook does **not** check whether the user is authenticated — the request will fail if the VA bearer is missing. Combine with `useShellAuth().isAuthenticated` to gate UI before calling.
* You don't need to add `axios` to your plugin's `package.json` if you only use it via this hook. Only add it if you need to import axios APIs directly (e.g. `axios.isAxiosError`).
