Skip to main content

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 returns a shared axios 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

import { useVaApi } from "@skyvexsoftware/stratos-sdk";

Signature

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

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

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:
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

CaseUse 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() — 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).