diff --git a/.env.development b/.env.development
index 61de4d2ff721795f2a01a4cbbedf8268c6794a44..419ea4b88290f568e51a96f8e2e2f9b150bb6bd2 100644
--- a/.env.development
+++ b/.env.development
@@ -1,4 +1,3 @@
 NEXT_PUBLIC_GL_CLIENT_ID=76df8d6ead161d6eb9b04d0bfeaca80ac4ad21b7d7bb69cf07b3ab17e17d5514
 NEXT_PUBLIC_GL_REDIRECT_URI=http://localhost:3211
 NEXT_PUBLIC_GL_URL=https://gitlab.inria.fr
-NEXT_PUBLIC_API_ROOT=https://public-api.grid5000.fr/stable
diff --git a/.env.production b/.env.production
index e0127bdb4e8ad421b03e21f8b210a3242d7b9ea5..f244e768e7d576b1dc392eeec70ee0aeade6868f 100644
--- a/.env.production
+++ b/.env.production
@@ -1,4 +1,3 @@
 NEXT_PUBLIC_GL_CLIENT_ID=76df8d6ead161d6eb9b04d0bfeaca80ac4ad21b7d7bb69cf07b3ab17e17d5514
 NEXT_PUBLIC_GL_REDIRECT_URI=https://grid5000.gitlabpages.inria.fr/pipeline-creation-assistant
 NEXT_PUBLIC_GL_URL=https://gitlab.inria.fr
-NEXT_PUBLIC_API_ROOT=https://public-api.grid5000.fr/stable
diff --git a/package-lock.json b/package-lock.json
index 50a445c29472357b7283433758e73a60f3d311a0..083518c78b012ba0f0a14ad3a2547b2e48592a28 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
         "@emotion/react": "^11.13.3",
         "@emotion/styled": "^11.13.0",
         "@fontsource/roboto": "^5.1.0",
+        "@grid5000/refrepo-ts": "^1.0.0",
         "@mui/icons-material": "^6.1.2",
         "@mui/material": "^6.1.2",
         "@mui/material-nextjs": "^6.1.2",
@@ -520,6 +521,11 @@
         "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
       }
     },
+    "node_modules/@grid5000/refrepo-ts": {
+      "version": "1.0.0",
+      "resolved": "https://gitlab.inria.fr/api/v4/projects/55430/packages/npm/@grid5000/refrepo-ts/-/@grid5000/refrepo-ts-1.0.0.tgz",
+      "integrity": "sha1-wsaCp781PnvO00mwjxYNLuNgm9s="
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.13.0",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
diff --git a/package.json b/package.json
index 20c5926d3e597b7be3e4a65b9c38312ce28e4d2f..cef2d21c33119ccbe1826cab46143003c13ab1a6 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
     "@emotion/react": "^11.13.3",
     "@emotion/styled": "^11.13.0",
     "@fontsource/roboto": "^5.1.0",
+    "@grid5000/refrepo-ts": "^1.0.0",
     "@mui/icons-material": "^6.1.2",
     "@mui/material": "^6.1.2",
     "@mui/material-nextjs": "^6.1.2",
diff --git a/src/components/TestImagesTabContent.tsx b/src/components/TestImagesTabContent.tsx
index f957d4ffda0dd6295aa34d7771188b5133562ced..db335a5d7daa9a4e8e5b86be51556db01d3d25ab 100644
--- a/src/components/TestImagesTabContent.tsx
+++ b/src/components/TestImagesTabContent.tsx
@@ -19,7 +19,7 @@ import ListItemIcon from '@mui/material/ListItemIcon';
 import ListItemText from '@mui/material/ListItemText';
 import ListSubheader from '@mui/material/ListSubheader';
 import LoadingRefApi from './LoadingRefApi';
-import { ObjectMap } from '@/lib/refrepo';
+import { ObjectMap } from '@grid5000/refrepo-ts';
 import RemoteBranchSelector from './RemoteBranchSelector';
 import Stack from '@mui/material/Stack';
 import Tooltip from '@mui/material/Tooltip';
diff --git a/src/hooks/useRefApi.ts b/src/hooks/useRefApi.ts
index 7560f0bca13143c0dd8ed8102c3bb24fc4427d14..34c8086d3ceb72c947f51c05280090d4a508f758 100644
--- a/src/hooks/useRefApi.ts
+++ b/src/hooks/useRefApi.ts
@@ -1,7 +1,6 @@
-import { APIResponse, ClusterNode, ClustersAPI, GPUDevice, Network, ObjectMap, OtherDevice, SitesAPI } from "@/lib/refrepo";
+import { APIResponse, ClusterNode, ClustersAPIResponse, GPUDevice, Network, ObjectMap, OtherDevice, SitesAPIResponse, clustersUrl, nodesUrl, sitesUrl } from "@grid5000/refrepo-ts";
 import { Architecture, platformTypeToOurArch } from "@/lib/config";
 import { Key, SWRHook } from "swr";
-import { clustersUrl, nodesUrl, sitesUrl } from "@/lib/routes";
 import { defaultFetch, fetchItems } from "@/lib/requests";
 import { isFirstOne } from "@/lib/generation";
 import { useMemo } from "react";
@@ -51,7 +50,7 @@ export type RefApiData = {
 
 function computeKeyFromClusters(
   getUrl: (e: string) => string,
-  response: APIResponse<ClustersAPI> | undefined
+  response: ClustersAPIResponse | undefined
 ) {
   if (response === undefined) {
     return undefined;
@@ -149,8 +148,8 @@ function nodesByIdMiddleware(useSWRNext: SWRHook) {
 }
 
 function computeClustersBySite(
-  sites: APIResponse<SitesAPI>,
-  clustersResponse: APIResponse<ClustersAPI>[],
+  sites: SitesAPIResponse,
+  clustersResponse: ClustersAPIResponse[],
 ): ClustersBySite {
   const clusters = clustersResponse.map(({ items }) => items.map(({ uid }) => uid));
 
@@ -167,8 +166,8 @@ export function useRefapi(branch: string = 'master'): RefApiData {
     data: sites, isLoading: sitesLoading, error: sitesError,
   } = useSWRImmutable(sitesUrl(branch), defaultFetch);
 
-  const branchClustersUrl = useMemo(() => clustersUrl.bind(null, branch), [branch]);
-  const branchNodesUrl = useMemo(() => nodesUrl.bind(null, branch), [branch]);
+  const branchClustersUrl = useMemo(() => (s: string) => clustersUrl(s, branch), [branch]);
+  const branchNodesUrl = useMemo(() => (s: string, c: string) => nodesUrl(s, c, branch), [branch]);
 
   const {
     data: clusters, isLoading: clustersLoading, error: clustersError,
diff --git a/src/lib/refrepo.ts b/src/lib/refrepo.ts
deleted file mode 100644
index 2cbc7dde979f6ec34bf4947de2e4cb1cb1c709d8..0000000000000000000000000000000000000000
--- a/src/lib/refrepo.ts
+++ /dev/null
@@ -1,190 +0,0 @@
-
-// FIXME: taken from accesses, we should definitely make a small ts lib
-
-export type ObjectMap<T> = {
-  [id: string]: T,
-};
-
-export type GPUDevice = {
-  model: string,
-  vendor: string,
-};
-
-export type OtherDevice = {
-  count: number,
-  memory: number,
-  model: string,
-  type: string,
-  vendor: string,
-};
-
-export type Processor = {
-  other_description: string,
-  model: string,
-  vendor: string,
-  microarchitecture: string,
-  version: string,
-  clock_speed: number,
-};
-
-export function getFamily({ model, vendor }: Processor): string {
-  return model.replace(`${vendor} `, '');
-}
-
-export type Architecture = {
-  nb_procs: number,
-  nb_cores: number,
-  nb_threads: number,
-  // NOTE: this doesn't seem future-proof.
-  platform_type: 'x86_64' | 'ppc64le' | 'aarch64',
-};
-
-export type RamSlot = {
-  size: number,
-  technology: 'dram' | 'pmem',
-};
-
-export type Storage = {
-  id: string,
-  interface: string,
-  storage: 'HDD' | 'SSD',
-  reservation?: boolean,
-  model: string,
-  vendor: string,
-  size: number,
-};
-
-export type Performance = {
-  node_flops: number,
-  core_flops: number,
-};
-
-export type Network = {
-  device: string,
-  driver: string,
-  interface: string,
-  kavlan: boolean,
-  enabled: boolean,
-  management: boolean,
-  model?: string,
-  mountable: boolean,
-  mounted: boolean,
-  rate?: number,
-  sriov: boolean,
-  sriov_totalvfs: number,
-  name: string,
-  vendor?: string,
-};
-
-export interface Identifiable {
-  uid: string,
-}
-
-export type ClusterNode = {
-  uid: string,
-  exotic: boolean,
-  chassis: {
-    name: string,
-    manufacturer: string,
-  },
-  gpu_devices?: ObjectMap<GPUDevice>,
-  other_devices?: ObjectMap<OtherDevice>,
-  memory_devices: RamSlot[],
-  storage_devices: Storage[],
-  network_adapters: Network[],
-  performance: Performance,
-  processor: Processor,
-  architecture: Architecture,
-  nodeset: string,
-};
-
-export type ClusterCommon = {
-  uid: string,
-  exotic: boolean,
-  model: string,
-  created_at: string,
-  manufactured_at: string,
-  queues: Array<string>,
-};
-
-export type Cluster = ClusterCommon & {
-  nodes: ObjectMap<ClusterNode>,
-};
-
-export type Site = {
-  uid: string,
-  clusters: ObjectMap<Cluster>,
-};
-
-export type RefRepoData = {
-  sites: ObjectMap<Site>,
-};
-
-export type SitesAPI = {
-  uid: string,
-};
-
-export type ClustersAPI = ClusterCommon;
-
-export type APIResponse<T> = {
-  items: T[];
-};
-
-export function getSitesUids(refrepo: RefRepoData): string[] {
-  return Object.values(refrepo.sites).map(({ uid }) => uid);
-}
-
-// This could also be name "make it sortable", and fixes the fact that numbers
-// in cluster names are not padded.
-export function normalizeClusterName(uid: string): string {
-  const asArray = Array.from(uid);
-  const firstNumber = asArray.findIndex(char => char >= '0' && char <= '9');
-  if (firstNumber === -1) {
-    return uid;
-  }
-  const name = uid.slice(0, firstNumber);
-  const number = uid.slice(firstNumber);
-  return `${name}${number.padStart(4, '0')}`;
-}
-
-//export async function buildCluster(site: string, cluster: ClustersAPI) {
-//const nodes =
-//(await defaultFetch(nodesUrl(site, cluster.uid))) as APIResponse<ClusterNode>;
-//const ret: Cluster = {
-//...cluster,
-//nodes: {},
-//};
-//nodes.items.forEach(n => ret.nodes[n.uid] = n);
-//return ret;
-//}
-
-//// The full refrepo is too big for nextjs's cache (>2MB), since we generate
-//// data at build time anyway, we might as well create a lot of cachable fetches.
-//export async function buildSite(s: string) {
-//const clusters = (await defaultFetch(clustersUrl(s))) as APIResponse<ClustersAPI>;
-
-//const site: Site = {
-//uid: s,
-//clusters: {},
-//};
-//for (const c of clusters.items) {
-//site.clusters[c.uid] = await buildCluster(s, c);
-//}
-//return site;
-//}
-
-//export async function getSites() {
-//const sites = (await defaultFetch(sitesUrl())) as APIResponse<SitesAPI>;
-//return sites.items.map(({ uid }) => uid);
-//}
-
-//export async function buildFullRefrepo(onlySites: string[]) {
-//const sites = onlySites.length > 0 ? onlySites : await getSites();
-//const refrepo: RefRepoData = {
-//sites: {},
-//};
-//for (const s of sites) {
-//refrepo.sites[s] = await buildSite(s);
-//}
-//return refrepo;
-//}
diff --git a/src/lib/routes.ts b/src/lib/routes.ts
deleted file mode 100644
index 9a89c92fe3858bfb82ebea375d57b518246019b2..0000000000000000000000000000000000000000
--- a/src/lib/routes.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-
-// TODO: withBranch(branch), adds ?branch=branch;
-export function apiUrl(path: string, branch: string | undefined = undefined) {
-  const appendUrl = branch ? `?branch=${branch}` : '';
-  return `${process.env.NEXT_PUBLIC_API_ROOT}${path}${appendUrl}`;
-}
-
-export function sitesUrl(branch: string) {
-  return apiUrl('/sites', branch);
-}
-
-export function clustersUrl(branch: string, site: string) {
-  return apiUrl(`/sites/${site}/clusters`, branch);
-}
-
-export function nodesUrl(branch: string, site: string, cluster: string) {
-  return apiUrl(`/sites/${site}/clusters/${cluster}/nodes`, branch);
-}