diff --git a/src/api.js b/src/api.js
deleted file mode 100644
index 3273951..0000000
--- a/src/api.js
+++ /dev/null
@@ -1,379 +0,0 @@
-import { DoesNotExistError } from "./types/internalErrors.js";
-import { getObjectKeys, removeDoubleSlash } from "./utils.js";
-import { pageState } from "./globalPageState.js";
-
-function getHeaders() {
-  return {
-    "X-Vault-Token": pageState.token,
-  }
-}
-
-const appendAPIURL = (url) => pageState.apiURL + url;
-
-export async function lookupSelf() {
-  const request = new Request(appendAPIURL("/v1/auth/token/lookup-self"), {
-    headers: getHeaders(),
-  });
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    if ("data" in data) {
-      return data.data;
-    } else if ("errors" in data) {
-      throw new Error(data.errors[0]);
-    }
-  });
-}
-
-export async function renewSelf() {
-  const request = new Request(appendAPIURL("/v1/auth/token/renew-self"), {
-    method: 'POST',
-    headers: {
-      ...getHeaders(),
-      'Content-Type': 'application/json'
-    },
-    body: JSON.stringify({})
-  });
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    if ("errors" in data) {
-      throw new Error(data.errors[0]);
-    }
-  });
-}
-
-export async function sealVault() {
-  const request = new Request(appendAPIURL("/v1/sys/seal"), {
-    method: 'PUT',
-    headers: getHeaders(),
-  });
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    if ("errors" in data) {
-      throw new Error(data.errors[0]);
-    }
-  });
-}
-
-export async function usernameLogin(username, password) {
-  const request = new Request(appendAPIURL(`/v1/auth/userpass/login/${username}`), {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json'
-    },
-    body: JSON.stringify({ "username": username, "password": password })
-  });
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    if ("auth" in data) {
-      return data.auth.client_token;
-    } else if ("errors" in data) {
-      throw new Error(data.errors[0]);
-    }
-  });
-}
-
-export async function getMounts() {
-  const request = new Request(appendAPIURL("/v1/sys/internal/ui/mounts"), {
-    headers: getHeaders(),
-  });
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    return data.data.secret;
-  });
-}
-
-export async function getSealStatus() {
-  const request = new Request(appendAPIURL("/v1/sys/seal-status"));
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    return data;
-  });
-}
-
-export async function submitUnsealKey(key) {
-  const request = new Request(appendAPIURL("/v1/sys/unseal"), {
-    method: "POST",
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      "key": key
-    })
-  });
-  let response = await fetch(request);
-  if (!response.ok) {
-    let json = await response.json();
-    throw new Error(json.errors[0]);
-  }
-}
-
-export async function getCapabilities(baseMount, secretPath, name) {
-  return await getCapabilitiesPath(removeDoubleSlash(baseMount + secretPath.join("/") + "/" + name))
-}
-
-export async function getCapabilitiesPath(path) {
-  const request = new Request(appendAPIURL("/v1/sys/capabilities-self"), {
-    method: "POST",
-    headers: {
-      'Content-Type': 'application/json',
-      ...getHeaders(),
-    },
-    body: JSON.stringify(
-      {
-        "paths": [removeDoubleSlash(path)]
-      }
-    )
-  });
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    return data.capabilities;
-  });
-}
-
-export async function getSecrets(baseMount, mountType, secretPath) {
-  let secretURL = "";
-  if (mountType == "kv-v2") {
-    secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}?list=true`;
-  } else {
-    // cubbyhole and v1 are identical
-    secretURL = `/v1/${baseMount}/${secretPath.join("")}?list=true`;
-  }
-  const request = new Request(appendAPIURL(secretURL), {
-    headers: getHeaders(),
-  });
-  return fetch(request).then(response => {
-    if (response.status == 404) {
-      throw DoesNotExistError;
-    }
-    return response.json();
-  }).then(data => {
-    return data.data.keys;
-  });
-
-}
-
-export async function getSecretMetadata(baseMount, secretPath, name) {
-  const request = new Request(appendAPIURL(`/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`), {
-    headers: getHeaders(),
-  });
-
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    return data.data;
-  });
-}
-
-export async function undeleteSecret(baseMount, secretPath, name, version = null) {
-  let secretURL = `/v1/${baseMount}/undelete/${secretPath.join("/")}/${name}`;
-  secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
-  if (version == null) {
-    let meta = await getSecretMetadata(
-      baseMount,
-      secretPath,
-      name
-    );
-    let versions = getObjectKeys(meta.versions);
-    version = String(versions[versions.length-1]);
-  }
-
-  let request = new Request(appendAPIURL(secretURL), {
-    method: "POST",
-    headers: {
-      ...getHeaders(),
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({ "versions": [version] })
-  });
-  let response = await fetch(request);
-  if (!response.ok) {
-    let json = await response.json();
-    throw new Error(json.errors[0]);
-  }
-}
-
-
-export async function getSecret(baseMount, mountType, secretPath, name, version = null) {
-  let secretURL = "";
-  if (mountType == "kv-v2") {
-    secretURL = `/v1/${baseMount}/data/${secretPath.join("")}/${name}`;
-    if (version != null) secretURL += `?version=${version}`;
-  } else {
-    secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
-  }
-  const request = new Request(appendAPIURL(secretURL), {
-    headers: getHeaders(),
-  });
-
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    return mountType == "kv-v2" ? data.data.data : data.data;
-  });
-}
-
-export async function deleteSecret(baseMount, mountType, secretPath, name, version = null) {
-  let secretURL = "";
-
-  let request;
-
-  if (mountType == "kv-v2" && version != null) {
-    secretURL = `/v1/${baseMount}/delete/${secretPath.join("")}/${name}`;
-    secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
-    request = new Request(appendAPIURL(secretURL), {
-      method: "POST",
-      headers: {
-        ...getHeaders(),
-        'Content-Type': 'application/json',
-      },
-      body: version != null ? JSON.stringify({ "versions": [version] }) : "{}"
-    });
-  } else {
-    if (mountType == "kv-v2") {
-      secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`;
-    } else {
-      secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
-    }
-    secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
-    request = new Request(appendAPIURL(secretURL), {
-      method: "DELETE",
-      headers: getHeaders(),
-    });
-  }
-
-
-  let response = await fetch(request);
-  if (!response.ok) {
-    let json = await response.json();
-    throw new Error(json.errors[0]);
-  }
-}
-
-export async function createOrUpdateSecret(baseMount, mountType, secretPath, name, data) {
-  let secretURL = "";
-  let APIData = {};
-
-  if (mountType == "kv-v2") {
-    secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`;
-    APIData = { "data": data };
-  } else {
-    secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`;
-    APIData = data;
-  }
-
-  secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
-  const request = new Request(appendAPIURL(secretURL), {
-    method: "POST",
-    headers: {
-      'Content-Type': 'application/json',
-      ...getHeaders(),
-    },
-    body: JSON.stringify(APIData, null, 0)
-  });
-  let response = await fetch(request);
-  if (!response.ok) {
-    let json = await response.json();
-    throw new Error(json.errors[0]);
-  }
-}
-
-export async function getTransitKeys(baseMount) {
-  const request = new Request(appendAPIURL(`/v1/${baseMount}/keys?list=true`), {
-    headers: getHeaders(),
-  });
-  return fetch(request).then(response => {
-    if (response.status == 404) {
-      throw DoesNotExistError;
-    }
-    return response.json();
-  }).then(data => {
-    return data.data.keys;
-  });
-}
-
-export async function transitEncrypt(baseMount, name, data) {
-  const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/encrypt/${name}`)), {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      ...getHeaders(),
-    },
-    body: JSON.stringify({plaintext: data})
-  });
-  let response = await fetch(request);
-  if (!response.ok) {
-    let json = await response.json();
-    throw new Error(json.errors[0]);
-  } else {
-    let json = await response.json();
-    return json.data;
-  }
-}
-
-export async function transitDecrypt(baseMount, name, data) {
-  const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/decrypt/${name}`)), {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      ...getHeaders(),
-    },
-    body: JSON.stringify({ciphertext: data})
-  });
-  let response = await fetch(request);
-  if (!response.ok) {
-    let json = await response.json();
-    throw new Error(json.errors[0]);
-  } else {
-    let json = await response.json();
-    return json.data;
-  }
-}
-
-
-export async function getTOTPKeys(baseMount) {
-  const request = new Request(appendAPIURL(`/v1/${baseMount}/keys?list=true`), {
-    headers: getHeaders(),
-  });
-  return fetch(request).then(response => {
-    if (response.status == 404) {
-      throw DoesNotExistError;
-    }
-    return response.json();
-  }).then(data => {
-    return data.data.keys;
-  });
-}
-
-export async function getTOTPCode(baseMount, name) {
-  const request = new Request(appendAPIURL(`/v1/${baseMount}/code/${name}`), {
-    headers: getHeaders(),
-  });
-  return fetch(request).then(response => {
-    return response.json();
-  }).then(data => {
-    return data.data.code;
-  });
-}
-
-export async function addNewTOTP(baseMount, parms) {
-  const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)), {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      ...getHeaders(),
-    },
-    body: JSON.stringify(parms)
-  });
-  let response = await fetch(request);
-  if (!response.ok) {
-    let json = await response.json();
-    throw new Error(json.errors[0]);
-  }
-}
diff --git a/src/api/addNewTOTP.js b/src/api/addNewTOTP.js
new file mode 100644
index 0000000..139e9ab
--- /dev/null
+++ b/src/api/addNewTOTP.js
@@ -0,0 +1,19 @@
+import { removeDoubleSlash } from "../utils.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function addNewTOTP(baseMount, parms) {
+  const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)), {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      ...getHeaders(),
+    },
+    body: JSON.stringify(parms)
+  });
+  let response = await fetch(request);
+  if (!response.ok) {
+    let json = await response.json();
+    throw new Error(json.errors[0]);
+  }
+}
diff --git a/src/api/apiUtils.js b/src/api/apiUtils.js
new file mode 100644
index 0000000..96a934e
--- /dev/null
+++ b/src/api/apiUtils.js
@@ -0,0 +1,9 @@
+import { pageState } from "../globalPageState.js";
+
+export function getHeaders() {
+  return {
+    "X-Vault-Token": pageState.token,
+  }
+}
+
+export const appendAPIURL = (url) => pageState.apiURL + url;
\ No newline at end of file
diff --git a/src/api/createOrUpdateSecret.js b/src/api/createOrUpdateSecret.js
new file mode 100644
index 0000000..0fa33dd
--- /dev/null
+++ b/src/api/createOrUpdateSecret.js
@@ -0,0 +1,31 @@
+import { removeDoubleSlash } from "../utils.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function createOrUpdateSecret(baseMount, mountType, secretPath, name, data) {
+  let secretURL = "";
+  let APIData = {};
+
+  if (mountType == "kv-v2") {
+    secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`;
+    APIData = { "data": data };
+  } else {
+    secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`;
+    APIData = data;
+  }
+
+  secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
+  const request = new Request(appendAPIURL(secretURL), {
+    method: "POST",
+    headers: {
+      'Content-Type': 'application/json',
+      ...getHeaders(),
+    },
+    body: JSON.stringify(APIData, null, 0)
+  });
+  let response = await fetch(request);
+  if (!response.ok) {
+    let json = await response.json();
+    throw new Error(json.errors[0]);
+  }
+}
diff --git a/src/api/deleteSecret.js b/src/api/deleteSecret.js
new file mode 100644
index 0000000..ac8648e
--- /dev/null
+++ b/src/api/deleteSecret.js
@@ -0,0 +1,38 @@
+import { removeDoubleSlash } from "../utils.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function deleteSecret(baseMount, mountType, secretPath, name, version = null) {
+  let secretURL = "";
+
+  let request;
+
+  if (mountType == "kv-v2" && version != null) {
+    secretURL = `/v1/${baseMount}/delete/${secretPath.join("")}/${name}`;
+    secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
+    request = new Request(appendAPIURL(secretURL), {
+      method: "POST",
+      headers: {
+        ...getHeaders(),
+        'Content-Type': 'application/json',
+      },
+      body: version != null ? JSON.stringify({ "versions": [version] }) : "{}"
+    });
+  } else {
+    if (mountType == "kv-v2") {
+      secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`;
+    } else {
+      secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
+    }
+    secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
+    request = new Request(appendAPIURL(secretURL), {
+      method: "DELETE",
+      headers: getHeaders(),
+    });
+  }
+  let response = await fetch(request);
+  if (!response.ok) {
+    let json = await response.json();
+    throw new Error(json.errors[0]);
+  }
+}
diff --git a/src/api/getCapabilities.js b/src/api/getCapabilities.js
new file mode 100644
index 0000000..d5d954b
--- /dev/null
+++ b/src/api/getCapabilities.js
@@ -0,0 +1,27 @@
+import { removeDoubleSlash } from "../utils.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getCapabilitiesPath(path) {
+  const request = new Request(appendAPIURL("/v1/sys/capabilities-self"), {
+    method: "POST",
+    headers: {
+      'Content-Type': 'application/json',
+      ...getHeaders(),
+    },
+    body: JSON.stringify(
+      {
+        "paths": [removeDoubleSlash(path)]
+      }
+    )
+  });
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    return data.capabilities;
+  });
+}
+
+export async function getCapabilities(baseMount, secretPath, name) {
+  return await getCapabilitiesPath(removeDoubleSlash(baseMount + secretPath.join("/") + "/" + name));
+}
diff --git a/src/api/getMounts.js b/src/api/getMounts.js
new file mode 100644
index 0000000..b4a62d2
--- /dev/null
+++ b/src/api/getMounts.js
@@ -0,0 +1,13 @@
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getMounts() {
+  const request = new Request(appendAPIURL("/v1/sys/internal/ui/mounts"), {
+    headers: getHeaders(),
+  });
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    return data.data.secret;
+  });
+}
diff --git a/src/api/getSealStatus.js b/src/api/getSealStatus.js
new file mode 100644
index 0000000..b0b0758
--- /dev/null
+++ b/src/api/getSealStatus.js
@@ -0,0 +1,11 @@
+import { appendAPIURL } from "./apiUtils.js";
+
+
+export async function getSealStatus() {
+  const request = new Request(appendAPIURL("/v1/sys/seal-status"));
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    return data;
+  });
+}
diff --git a/src/api/getSecret.js b/src/api/getSecret.js
new file mode 100644
index 0000000..92a8829
--- /dev/null
+++ b/src/api/getSecret.js
@@ -0,0 +1,22 @@
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getSecret(baseMount, mountType, secretPath, name, version = null) {
+  let secretURL = "";
+  if (mountType == "kv-v2") {
+    secretURL = `/v1/${baseMount}/data/${secretPath.join("")}/${name}`;
+    if (version != null)
+      secretURL += `?version=${version}`;
+  } else {
+    secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
+  }
+  const request = new Request(appendAPIURL(secretURL), {
+    headers: getHeaders(),
+  });
+
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    return mountType == "kv-v2" ? data.data.data : data.data;
+  });
+}
diff --git a/src/api/getSecretMetadata.js b/src/api/getSecretMetadata.js
new file mode 100644
index 0000000..ec67292
--- /dev/null
+++ b/src/api/getSecretMetadata.js
@@ -0,0 +1,14 @@
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getSecretMetadata(baseMount, secretPath, name) {
+  const request = new Request(appendAPIURL(`/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`), {
+    headers: getHeaders(),
+  });
+
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    return data.data;
+  });
+}
diff --git a/src/api/getSecrets.js b/src/api/getSecrets.js
new file mode 100644
index 0000000..2ea6b11
--- /dev/null
+++ b/src/api/getSecrets.js
@@ -0,0 +1,24 @@
+import { DoesNotExistError } from "../types/internalErrors.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getSecrets(baseMount, mountType, secretPath) {
+  let secretURL = "";
+  if (mountType == "kv-v2") {
+    secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}?list=true`;
+  } else {
+    // cubbyhole and v1 are identical
+    secretURL = `/v1/${baseMount}/${secretPath.join("")}?list=true`;
+  }
+  const request = new Request(appendAPIURL(secretURL), {
+    headers: getHeaders(),
+  });
+  return fetch(request).then(response => {
+    if (response.status == 404) {
+      throw DoesNotExistError;
+    }
+    return response.json();
+  }).then(data => {
+    return data.data.keys;
+  });
+}
diff --git a/src/api/getTOTPCode.js b/src/api/getTOTPCode.js
new file mode 100644
index 0000000..bae6473
--- /dev/null
+++ b/src/api/getTOTPCode.js
@@ -0,0 +1,13 @@
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getTOTPCode(baseMount, name) {
+  const request = new Request(appendAPIURL(`/v1/${baseMount}/code/${name}`), {
+    headers: getHeaders(),
+  });
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    return data.data.code;
+  });
+}
diff --git a/src/api/getTOTPKeys.js b/src/api/getTOTPKeys.js
new file mode 100644
index 0000000..c470866
--- /dev/null
+++ b/src/api/getTOTPKeys.js
@@ -0,0 +1,17 @@
+import { DoesNotExistError } from "../types/internalErrors.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getTOTPKeys(baseMount) {
+  const request = new Request(appendAPIURL(`/v1/${baseMount}/keys?list=true`), {
+    headers: getHeaders(),
+  });
+  return fetch(request).then(response => {
+    if (response.status == 404) {
+      throw DoesNotExistError;
+    }
+    return response.json();
+  }).then(data => {
+    return data.data.keys;
+  });
+}
diff --git a/src/api/getTransitKeys.js b/src/api/getTransitKeys.js
new file mode 100644
index 0000000..d7454ce
--- /dev/null
+++ b/src/api/getTransitKeys.js
@@ -0,0 +1,17 @@
+import { DoesNotExistError } from "../types/internalErrors.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function getTransitKeys(baseMount) {
+  const request = new Request(appendAPIURL(`/v1/${baseMount}/keys?list=true`), {
+    headers: getHeaders(),
+  });
+  return fetch(request).then(response => {
+    if (response.status == 404) {
+      throw DoesNotExistError;
+    }
+    return response.json();
+  }).then(data => {
+    return data.data.keys;
+  });
+}
diff --git a/src/api/index.js b/src/api/index.js
new file mode 100644
index 0000000..061eaf6
--- /dev/null
+++ b/src/api/index.js
@@ -0,0 +1,23 @@
+export * from "addNewTOTP.js";
+export * from "api.js";
+export * from "apiUtils.js";
+export * from "createOrUpdateSecret.js";
+export * from "deleteSecret.js";
+export * from "getCapabilities.js";
+export * from "getCapabilitiesPath.js";
+export * from "getMounts.js";
+export * from "getSealStatus.js";
+export * from "getSecret.js";
+export * from "getSecretMetadata.js";
+export * from "getSecrets.js";
+export * from "getTOTPCode.js";
+export * from "getTOTPKeys.js";
+export * from "getTransitKeys.js";
+export * from "lookupSelf.js";
+export * from "renewSelf.js";
+export * from "sealVault.js";
+export * from "submitUnsealKey.js";
+export * from "transitDecrypt.js";
+export * from "transitEncrypt.js";
+export * from "undeleteSecret.js";
+export * from "usernameLogin.js";
\ No newline at end of file
diff --git a/src/api/lookupSelf.js b/src/api/lookupSelf.js
new file mode 100644
index 0000000..f54f9c5
--- /dev/null
+++ b/src/api/lookupSelf.js
@@ -0,0 +1,17 @@
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function lookupSelf() {
+  const request = new Request(appendAPIURL("/v1/auth/token/lookup-self"), {
+    headers: getHeaders(),
+  });
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    if ("data" in data) {
+      return data.data;
+    } else if ("errors" in data) {
+      throw new Error(data.errors[0]);
+    }
+  });
+}
diff --git a/src/api/renewSelf.js b/src/api/renewSelf.js
new file mode 100644
index 0000000..fee7c08
--- /dev/null
+++ b/src/api/renewSelf.js
@@ -0,0 +1,20 @@
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function renewSelf() {
+  const request = new Request(appendAPIURL("/v1/auth/token/renew-self"), {
+    method: 'POST',
+    headers: {
+      ...getHeaders(),
+      'Content-Type': 'application/json'
+    },
+    body: JSON.stringify({})
+  });
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    if ("errors" in data) {
+      throw new Error(data.errors[0]);
+    }
+  });
+}
diff --git a/src/api/sealVault.js b/src/api/sealVault.js
new file mode 100644
index 0000000..40a14da
--- /dev/null
+++ b/src/api/sealVault.js
@@ -0,0 +1,16 @@
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function sealVault() {
+  const request = new Request(appendAPIURL("/v1/sys/seal"), {
+    method: 'PUT',
+    headers: getHeaders(),
+  });
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    if ("errors" in data) {
+      throw new Error(data.errors[0]);
+    }
+  });
+}
diff --git a/src/api/submitUnsealKey.js b/src/api/submitUnsealKey.js
new file mode 100644
index 0000000..e29e1f4
--- /dev/null
+++ b/src/api/submitUnsealKey.js
@@ -0,0 +1,19 @@
+import { appendAPIURL } from "./apiUtils.js";
+
+
+export async function submitUnsealKey(key) {
+  const request = new Request(appendAPIURL("/v1/sys/unseal"), {
+    method: "POST",
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify({
+      "key": key
+    })
+  });
+  let response = await fetch(request);
+  if (!response.ok) {
+    let json = await response.json();
+    throw new Error(json.errors[0]);
+  }
+}
diff --git a/src/api/transitDecrypt.js b/src/api/transitDecrypt.js
new file mode 100644
index 0000000..f74888e
--- /dev/null
+++ b/src/api/transitDecrypt.js
@@ -0,0 +1,22 @@
+import { removeDoubleSlash } from "../utils.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function transitDecrypt(baseMount, name, data) {
+  const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/decrypt/${name}`)), {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      ...getHeaders(),
+    },
+    body: JSON.stringify({ ciphertext: data })
+  });
+  let response = await fetch(request);
+  if (!response.ok) {
+    let json = await response.json();
+    throw new Error(json.errors[0]);
+  } else {
+    let json = await response.json();
+    return json.data;
+  }
+}
diff --git a/src/api/transitEncrypt.js b/src/api/transitEncrypt.js
new file mode 100644
index 0000000..b3e98ca
--- /dev/null
+++ b/src/api/transitEncrypt.js
@@ -0,0 +1,22 @@
+import { removeDoubleSlash } from "../utils.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+
+
+export async function transitEncrypt(baseMount, name, data) {
+  const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/encrypt/${name}`)), {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      ...getHeaders(),
+    },
+    body: JSON.stringify({ plaintext: data })
+  });
+  let response = await fetch(request);
+  if (!response.ok) {
+    let json = await response.json();
+    throw new Error(json.errors[0]);
+  } else {
+    let json = await response.json();
+    return json.data;
+  }
+}
diff --git a/src/api/undeleteSecret.js b/src/api/undeleteSecret.js
new file mode 100644
index 0000000..7ae2471
--- /dev/null
+++ b/src/api/undeleteSecret.js
@@ -0,0 +1,32 @@
+import { getObjectKeys, removeDoubleSlash } from "../utils.js";
+import { appendAPIURL, getHeaders } from "./apiUtils.js";
+import { getSecretMetadata } from "./getSecretMetadata";
+
+
+export async function undeleteSecret(baseMount, secretPath, name, version = null) {
+  let secretURL = `/v1/${baseMount}/undelete/${secretPath.join("/")}/${name}`;
+  secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
+  if (version == null) {
+    let meta = await getSecretMetadata(
+      baseMount,
+      secretPath,
+      name
+    );
+    let versions = getObjectKeys(meta.versions);
+    version = String(versions[versions.length - 1]);
+  }
+
+  let request = new Request(appendAPIURL(secretURL), {
+    method: "POST",
+    headers: {
+      ...getHeaders(),
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify({ "versions": [version] })
+  });
+  let response = await fetch(request);
+  if (!response.ok) {
+    let json = await response.json();
+    throw new Error(json.errors[0]);
+  }
+}
diff --git a/src/api/usernameLogin.js b/src/api/usernameLogin.js
new file mode 100644
index 0000000..afea14f
--- /dev/null
+++ b/src/api/usernameLogin.js
@@ -0,0 +1,21 @@
+import { appendAPIURL } from "./apiUtils.js";
+
+
+export async function usernameLogin(username, password) {
+  const request = new Request(appendAPIURL(`/v1/auth/userpass/login/${username}`), {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json'
+    },
+    body: JSON.stringify({ "username": username, "password": password })
+  });
+  return fetch(request).then(response => {
+    return response.json();
+  }).then(data => {
+    if ("auth" in data) {
+      return data.auth.client_token;
+    } else if ("errors" in data) {
+      throw new Error(data.errors[0]);
+    }
+  });
+}
diff --git a/src/main.js b/src/main.js
index 696c5a5..d026043 100644
--- a/src/main.js
+++ b/src/main.js
@@ -17,7 +17,7 @@ import {
   changePage,
   renderPage,
 } from "./pageUtils.js";
-import { getSealStatus } from './api.js';
+import { getSealStatus } from "./api/getSealStatus";
 import { makeElement } from "./htmlUtils.js";
 import { pageState } from "./globalPageState.js";
 
diff --git a/src/pageUtils.js b/src/pageUtils.js
index f414d01..e63cc07 100644
--- a/src/pageUtils.js
+++ b/src/pageUtils.js
@@ -1,4 +1,5 @@
-import { getSealStatus, lookupSelf } from './api.js';
+import { getSealStatus } from "./api/getSealStatus";
+import { lookupSelf } from "./api/lookupSelf";
 import { makeElement } from "./htmlUtils.js";
 import { pageState } from "./globalPageState.js";
 import UIkit from 'uikit/dist/js/uikit.min.js';
diff --git a/src/pages/Home.js b/src/pages/Home.js
index b1ba22e..6c12401 100644
--- a/src/pages/Home.js
+++ b/src/pages/Home.js
@@ -1,6 +1,7 @@
 import { Page } from "../types/Page.js";
 import { changePage, prePageChecks, setErrorText } from "../pageUtils.js";
-import { getMounts, lookupSelf } from "../api.js";
+import { getMounts } from "../api/getMounts";
+import { lookupSelf } from "../api/lookupSelf";
 import { makeElement } from "../htmlUtils.js";
 import { pageState } from "../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/KeyValue/KeyValueDelete.js b/src/pages/KeyValue/KeyValueDelete.js
index 0f0626a..a18c123 100644
--- a/src/pages/KeyValue/KeyValueDelete.js
+++ b/src/pages/KeyValue/KeyValueDelete.js
@@ -1,6 +1,6 @@
 import { Page } from "../../types/Page.js";
 import { changePage, setPageContent, setTitleElement } from "../../pageUtils.js";
-import { deleteSecret } from "../../api.js";
+import { deleteSecret } from "../../api/deleteSecret";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/KeyValue/KeyValueNew.js b/src/pages/KeyValue/KeyValueNew.js
index 5aa5c82..6adad38 100644
--- a/src/pages/KeyValue/KeyValueNew.js
+++ b/src/pages/KeyValue/KeyValueNew.js
@@ -1,6 +1,6 @@
 import { Page } from "../../types/Page.js";
 import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils.js";
-import { createOrUpdateSecret } from "../../api.js";
+import { createOrUpdateSecret } from "../../api/createOrUpdateSecret";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/KeyValue/KeyValueSecret.js b/src/pages/KeyValue/KeyValueSecret.js
index 691c9fa..883acd7 100644
--- a/src/pages/KeyValue/KeyValueSecret.js
+++ b/src/pages/KeyValue/KeyValueSecret.js
@@ -1,7 +1,9 @@
 import { CopyableInputBox } from "../../elements/CopyableInputBox.js";
 import { Page } from "../../types/Page.js";
 import { changePage, setPageContent, setTitleElement } from "../../pageUtils.js";
-import { getCapabilities, getSecret, undeleteSecret } from "../../api.js";
+import { getSecret } from "../../api/getSecret";
+import { undeleteSecret } from "../../api/undeleteSecret";
+import { getCapabilities } from "../../api/getCapabilities";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import Prism from "prismjs";
diff --git a/src/pages/KeyValue/KeyValueSecretsEdit.js b/src/pages/KeyValue/KeyValueSecretsEdit.js
index 4bffc58..708ad2e 100644
--- a/src/pages/KeyValue/KeyValueSecretsEdit.js
+++ b/src/pages/KeyValue/KeyValueSecretsEdit.js
@@ -1,7 +1,8 @@
 import { CodeJar } from "codejar";
 import { Page } from "../../types/Page.js";
 import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils.js";
-import { createOrUpdateSecret, getSecret } from "../../api.js";
+import { createOrUpdateSecret } from "../../api/createOrUpdateSecret.js";
+import { getSecret } from "../../api/getSecret.js";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import { verifyJSONString } from "../../utils.js";
diff --git a/src/pages/KeyValue/KeyValueVersions.js b/src/pages/KeyValue/KeyValueVersions.js
index 53e8dea..440237c 100644
--- a/src/pages/KeyValue/KeyValueVersions.js
+++ b/src/pages/KeyValue/KeyValueVersions.js
@@ -1,6 +1,6 @@
 import { Page } from "../../types/Page.js";
 import { changePage, setPageContent, setTitleElement } from "../../pageUtils.js";
-import { getSecretMetadata } from "../../api.js";
+import { getSecretMetadata } from "../../api/getSecretMetadata.js";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/KeyValue/KeyValueView.js b/src/pages/KeyValue/KeyValueView.js
index 4ff95fa..813e5cd 100644
--- a/src/pages/KeyValue/KeyValueView.js
+++ b/src/pages/KeyValue/KeyValueView.js
@@ -1,7 +1,7 @@
 import { DoesNotExistError } from "../../types/internalErrors.js";
 import { Page } from "../../types/Page.js";
 import { changePage, setErrorText, setTitleElement } from "../../pageUtils.js";
-import { getSecrets } from "../../api.js";
+import { getSecrets } from "../../api/getSecrets";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/Login.js b/src/pages/Login.js
index 1441d25..9618394 100644
--- a/src/pages/Login.js
+++ b/src/pages/Login.js
@@ -2,7 +2,8 @@ import { Margin } from "../elements/Margin.js";
 import { MarginInline } from "../elements/MarginInline.js";
 import { Page } from "../types/Page.js";
 import { changePage, setErrorText, setPageContent } from "../pageUtils.js";
-import { lookupSelf, usernameLogin } from "../api.js";
+import { usernameLogin } from "../api/usernameLogin";
+import { lookupSelf } from "../api/lookupSelf";
 import { makeElement } from "../htmlUtils.js";
 import { pageState } from "../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/Me.js b/src/pages/Me.js
index 42c91fb..02e3041 100644
--- a/src/pages/Me.js
+++ b/src/pages/Me.js
@@ -1,6 +1,8 @@
 import { Page } from "../types/Page.js";
 import { addClipboardNotifications, changePage, prePageChecks, setErrorText, setPageContent } from "../pageUtils.js";
-import { getCapabilitiesPath, renewSelf, sealVault } from "../api.js";
+import { getCapabilitiesPath } from "../api/getCapabilities.js";
+import { renewSelf } from "../api/renewSelf.js";
+import { sealVault } from "../api/sealVault.js";
 import { makeElement } from "../htmlUtils.js";
 import { pageState } from "../globalPageState.js";
 import ClipboardJS from "clipboard";
diff --git a/src/pages/TOTP/NewTOTP.js b/src/pages/TOTP/NewTOTP.js
index a74f645..da3acd5 100644
--- a/src/pages/TOTP/NewTOTP.js
+++ b/src/pages/TOTP/NewTOTP.js
@@ -1,12 +1,13 @@
 import { Margin } from "../../elements/Margin.js";
 import { MarginInline } from "../../elements/MarginInline.js";
 import { Page } from "../../types/Page.js";
-import { addNewTOTP } from "../../api.js";
+import { addNewTOTP } from "../../api/addNewTOTP";
 import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils.js";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import i18next from 'i18next';
 
+
 export class NewTOTPPage extends Page {
   constructor() {
     super();
diff --git a/src/pages/TOTP/TOTPView.js b/src/pages/TOTP/TOTPView.js
index a209596..948a604 100644
--- a/src/pages/TOTP/TOTPView.js
+++ b/src/pages/TOTP/TOTPView.js
@@ -2,7 +2,8 @@ import { CopyableInputBox } from "../../elements/CopyableInputBox.js";
 import { DoesNotExistError } from "../../types/internalErrors.js";
 import { Page } from "../../types/Page.js";
 import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils.js";
-import { getTOTPCode, getTOTPKeys } from "../../api.js";
+import { getTOTPCode } from "../../api/getTOTPCode";
+import { getTOTPKeys } from "../../api/getTOTPKeys";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/Transit/TransitDecrypt.js b/src/pages/Transit/TransitDecrypt.js
index fb3ab8a..f0be0d1 100644
--- a/src/pages/Transit/TransitDecrypt.js
+++ b/src/pages/Transit/TransitDecrypt.js
@@ -4,7 +4,7 @@ import { Page } from "../../types/Page.js";
 import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils.js";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
-import { transitDecrypt } from "../../api.js";
+import { transitDecrypt } from "../../api/transitDecrypt";
 import UIkit from 'uikit/dist/js/uikit.min.js';
 import i18next from "i18next";
 
diff --git a/src/pages/Transit/TransitEncrypt.js b/src/pages/Transit/TransitEncrypt.js
index 9fc32a0..3ca6a23 100644
--- a/src/pages/Transit/TransitEncrypt.js
+++ b/src/pages/Transit/TransitEncrypt.js
@@ -4,7 +4,7 @@ import { Page } from "../../types/Page.js";
 import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils.js";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
-import { transitEncrypt } from "../../api.js";
+import { transitEncrypt } from "../../api/transitEncrypt";
 import UIkit from 'uikit/dist/js/uikit.min.js';
 import i18next from "i18next";
 
diff --git a/src/pages/Transit/TransitView.js b/src/pages/Transit/TransitView.js
index 3928f56..e6fd9a9 100644
--- a/src/pages/Transit/TransitView.js
+++ b/src/pages/Transit/TransitView.js
@@ -1,7 +1,7 @@
 import { DoesNotExistError } from "../../types/internalErrors.js";
 import { Page } from "../../types/Page.js";
 import { changePage, setErrorText, setTitleElement } from "../../pageUtils.js";
-import { getTransitKeys } from "../../api.js";
+import { getTransitKeys } from "../../api/getTransitKeys";
 import { makeElement } from "../../htmlUtils.js";
 import { pageState } from "../../globalPageState.js";
 import i18next from 'i18next';
diff --git a/src/pages/Unseal.js b/src/pages/Unseal.js
index a7ccfba..ad89924 100644
--- a/src/pages/Unseal.js
+++ b/src/pages/Unseal.js
@@ -2,7 +2,8 @@ import { MarginInline } from "../elements/MarginInline.js";
 import { Page } from "../types/Page.js";
 import { QRScanner } from "../elements/QRScanner.js";
 import { changePage, setErrorText, setPageContent } from "../pageUtils.js";
-import { getSealStatus, submitUnsealKey } from "../api.js";
+import { getSealStatus } from "../api/getSealStatus.js";
+import { submitUnsealKey } from "../api/submitUnsealKey.js";
 import { makeElement } from "../htmlUtils.js";
 import i18next from 'i18next';