diff --git a/docs-site/docs/features/reactive-resume.md b/docs-site/docs/features/reactive-resume.md
index 73adaa5..a7dd8b8 100644
--- a/docs-site/docs/features/reactive-resume.md
+++ b/docs-site/docs/features/reactive-resume.md
@@ -94,6 +94,7 @@ Important:
Configure in **Settings → Reactive Resume**:
- `rxresumeMode` (`v5` or `v4`)
+- `rxresumeUrl` (optional shared URL for cloud or self-hosted deployments)
- `rxresumeApiKey` (for v5)
- `rxresumeEmail` + `rxresumePassword` (for v4)
@@ -105,6 +106,8 @@ Or via environment variables:
- `RXRESUME_PASSWORD`
- optional `RXRESUME_URL` (works for both modes; v5 OpenAPI path is added automatically)
+If you leave the URL blank in the dashboard, JobOps uses `RXRESUME_URL` when it is set; if not set, it falls back to the public cloud default for the selected mode.
+
### 2) Select base resume
In **Settings → Reactive Resume**:
diff --git a/docs-site/docs/features/settings.md b/docs-site/docs/features/settings.md
index 36eebe1..f6688b5 100644
--- a/docs-site/docs/features/settings.md
+++ b/docs-site/docs/features/settings.md
@@ -109,6 +109,8 @@ Defaults and constraints:

+- Configure a shared RxResume URL for cloud or self-hosted deployments
+- Configure v4 email/password or v5 API key in the same section
- Select a template/base resume
- Configure project selection behavior:
- Max projects
@@ -210,7 +212,9 @@ curl -X POST "http://localhost:3001/api/backups"
### RxResume controls are disabled
-- Configure RxResume credentials in Environment & Accounts first.
+- JobOps resolves the RxResume URL in this order: the value saved in **Settings → Reactive Resume**, then the `RXRESUME_URL` environment variable (if set), and finally the public cloud default.
+- Open **Settings → Reactive Resume** and configure the shared RxResume URL if you use a self-hosted instance.
+- If you leave the URL blank, JobOps will fall back to `RXRESUME_URL` when it is configured; otherwise it uses the public cloud default.
- Then refresh available resumes from the Reactive Resume section.
### RxResume projects look empty in the RxResume UI
diff --git a/orchestrator/src/client/components/OnboardingGate.test.tsx b/orchestrator/src/client/components/OnboardingGate.test.tsx
index 84affaf..c5285f5 100644
--- a/orchestrator/src/client/components/OnboardingGate.test.tsx
+++ b/orchestrator/src/client/components/OnboardingGate.test.tsx
@@ -1,6 +1,6 @@
import * as api from "@client/api";
import { useSettings } from "@client/hooks/useSettings";
-import { screen, waitFor } from "@testing-library/react";
+import { fireEvent, screen, waitFor } from "@testing-library/react";
import type React from "react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { renderWithQueryClient } from "../test/renderWithQueryClient";
@@ -22,7 +22,18 @@ vi.mock("@client/hooks/useSettings", () => ({
}));
vi.mock("@client/pages/settings/components/SettingsInput", () => ({
- SettingsInput: ({ label }: { label: string }) =>
{label}
,
+ SettingsInput: ({
+ label,
+ inputProps,
+ }: {
+ label: string;
+ inputProps?: React.InputHTMLAttributes;
+ }) => (
+
+ {label}
+
+
+ ),
}));
vi.mock("@client/pages/settings/components/BaseResumeSelection", () => ({
@@ -95,6 +106,7 @@ const settingsResponse = {
llmProvider: { value: "openrouter", default: "openrouter", override: null },
llmApiKeyHint: null,
rxresumeEmail: "",
+ rxresumeUrl: "",
rxresumeApiKeyHint: null,
rxresumePasswordHint: null,
rxresumeBaseResumeId: null,
@@ -193,4 +205,48 @@ describe("OnboardingGate", () => {
});
expect(screen.queryByText("LLM API key")).not.toBeInTheDocument();
});
+
+ it("renders the RxResume URL field and includes it in validation", async () => {
+ vi.mocked(useSettings).mockReturnValue({
+ ...settingsResponse,
+ settings: {
+ ...settingsResponse.settings,
+ rxresumeUrl: "https://resume.example.com",
+ rxresumeApiKeyHint: "abcd1234",
+ },
+ } as any);
+ vi.mocked(api.validateLlm).mockResolvedValue({
+ valid: false,
+ message: "Invalid",
+ });
+ vi.mocked(api.validateRxresume).mockResolvedValue({
+ valid: true,
+ message: null,
+ });
+ vi.mocked(api.validateResumeConfig).mockResolvedValue({
+ valid: true,
+ message: null,
+ });
+
+ render( );
+
+ await waitFor(() =>
+ expect(screen.getByLabelText("RxResume URL")).toBeInTheDocument(),
+ );
+ await waitFor(() =>
+ expect(api.validateRxresume).toHaveBeenCalledWith(
+ expect.objectContaining({
+ baseUrl: "https://resume.example.com",
+ }),
+ ),
+ );
+
+ fireEvent.change(screen.getByLabelText("RxResume URL"), {
+ target: { value: "https://self-hosted.example.com" },
+ });
+
+ expect(
+ screen.getByDisplayValue("https://self-hosted.example.com"),
+ ).toBeInTheDocument();
+ });
});
diff --git a/orchestrator/src/client/components/OnboardingGate.tsx b/orchestrator/src/client/components/OnboardingGate.tsx
index 0aae3b6..b3dfc66 100644
--- a/orchestrator/src/client/components/OnboardingGate.tsx
+++ b/orchestrator/src/client/components/OnboardingGate.tsx
@@ -59,6 +59,7 @@ type OnboardingFormData = {
llmApiKey: string;
rxresumeMode: RxResumeMode;
rxresumeEmail: string;
+ rxresumeUrl: string;
rxresumePassword: string;
rxresumeApiKey: string;
rxresumeBaseResumeId: string | null;
@@ -135,6 +136,7 @@ export const OnboardingGate: React.FC = () => {
llmApiKey: "",
rxresumeMode: "v5",
rxresumeEmail: "",
+ rxresumeUrl: "",
rxresumePassword: "",
rxresumeApiKey: "",
rxresumeBaseResumeId: null,
@@ -287,6 +289,7 @@ export const OnboardingGate: React.FC = () => {
llmApiKey: "",
rxresumeMode: initialMode,
rxresumeEmail: "",
+ rxresumeUrl: settings.rxresumeUrl ?? "",
rxresumePassword: "",
rxresumeApiKey: "",
rxresumeBaseResumeId: selectedId,
@@ -769,6 +772,10 @@ export const OnboardingGate: React.FC = () => {
apiKey: watch("rxresumeApiKey"),
onApiKeyChange: (value) => setValue("rxresumeApiKey", value),
}}
+ shared={{
+ baseUrl: watch("rxresumeUrl"),
+ onBaseUrlChange: (value) => setValue("rxresumeUrl", value),
+ }}
v4={{
email: watch("rxresumeEmail"),
onEmailChange: (value) => setValue("rxresumeEmail", value),
diff --git a/orchestrator/src/client/components/ReactiveResumeConfigPanel.tsx b/orchestrator/src/client/components/ReactiveResumeConfigPanel.tsx
index d5e2a5d..1a8c2ba 100644
--- a/orchestrator/src/client/components/ReactiveResumeConfigPanel.tsx
+++ b/orchestrator/src/client/components/ReactiveResumeConfigPanel.tsx
@@ -62,6 +62,13 @@ type ReactiveResumeConfigPanelProps = {
helper?: string;
placeholder?: string;
};
+ shared: {
+ baseUrl: string;
+ onBaseUrlChange: (value: string) => void;
+ baseUrlError?: string;
+ baseUrlHelper?: string;
+ baseUrlPlaceholder?: string;
+ };
v4: {
email: string;
onEmailChange: (value: string) => void;
@@ -110,6 +117,7 @@ export const ReactiveResumeConfigPanel: React.FC<
showValidationStatus = false,
validationStatuses,
intro,
+ shared,
v5,
v4,
projectSelection,
@@ -151,6 +159,25 @@ export const ReactiveResumeConfigPanel: React.FC<
{mode === "v5" ? (
+
+ shared.onBaseUrlChange(event.currentTarget.value),
+ }}
+ type="url"
+ placeholder={
+ shared.baseUrlPlaceholder ?? "https://resume.example.com"
+ }
+ helper={
+ shared.baseUrlHelper ??
+ "Leave blank to use the default for the selected mode (or the RXRESUME_URL environment override, if set)."
+ }
+ disabled={disabled}
+ error={shared.baseUrlError}
+ />
) : (
+
+
+ shared.onBaseUrlChange(event.currentTarget.value),
+ }}
+ type="url"
+ placeholder={
+ shared.baseUrlPlaceholder ?? "https://resume.example.com"
+ }
+ helper={
+ shared.baseUrlHelper ??
+ "Leave blank to use the public cloud default for the selected mode."
+ }
+ disabled={disabled}
+ error={shared.baseUrlError}
+ />
+
({
email: input.rxresumeEmail?.trim() ?? "",
+ baseUrl: input.rxresumeUrl?.trim() ?? "",
password: input.rxresumePassword?.trim() ?? "",
apiKey: input.rxresumeApiKey?.trim() ?? "",
});
@@ -116,6 +119,7 @@ export const toRxResumeValidationPayload = (
draft: RxResumeCredentialDrafts,
) => ({
email: draft.email || undefined,
+ baseUrl: draft.baseUrl || undefined,
password: draft.password || undefined,
apiKey: draft.apiKey || undefined,
});
@@ -126,6 +130,7 @@ export const buildRxResumeSettingsUpdate = (
): Partial => {
const update: Partial = {
rxresumeMode: mode,
+ rxresumeUrl: draft.baseUrl || null,
};
if (draft.email) update.rxresumeEmail = draft.email;
if (draft.password) update.rxresumePassword = draft.password;
diff --git a/orchestrator/src/client/pages/SettingsPage.test.tsx b/orchestrator/src/client/pages/SettingsPage.test.tsx
index 968ca90..8d64155 100644
--- a/orchestrator/src/client/pages/SettingsPage.test.tsx
+++ b/orchestrator/src/client/pages/SettingsPage.test.tsx
@@ -276,6 +276,38 @@ describe("SettingsPage", () => {
);
});
+ it("saves a shared RxResume URL from the Reactive Resume section", async () => {
+ vi.mocked(api.getSettings).mockResolvedValue(baseSettings);
+ vi.mocked(api.updateSettings).mockResolvedValue({
+ ...baseSettings,
+ rxresumeUrl: "https://resume.example.com",
+ });
+
+ renderPage();
+
+ const reactiveResumeTrigger = await screen.findByRole("button", {
+ name: /reactive resume/i,
+ });
+ fireEvent.click(reactiveResumeTrigger);
+
+ const urlInput = screen.getByLabelText(/rxresume url/i);
+ await waitFor(() => expect(urlInput).toBeEnabled());
+ fireEvent.change(urlInput, {
+ target: { value: "https://resume.example.com" },
+ });
+
+ const saveButton = screen.getByRole("button", { name: /^save$/i });
+ await waitFor(() => expect(saveButton).toBeEnabled());
+ fireEvent.click(saveButton);
+
+ await waitFor(() => expect(api.updateSettings).toHaveBeenCalled());
+ expect(api.updateSettings).toHaveBeenCalledWith(
+ expect.objectContaining({
+ rxresumeUrl: "https://resume.example.com",
+ }),
+ );
+ });
+
it("saves the writing language mode through the settings page", async () => {
vi.mocked(api.getSettings).mockResolvedValue(baseSettings);
vi.mocked(api.updateSettings).mockResolvedValue(
diff --git a/orchestrator/src/client/pages/SettingsPage.tsx b/orchestrator/src/client/pages/SettingsPage.tsx
index 3a68a7c..8aa222e 100644
--- a/orchestrator/src/client/pages/SettingsPage.tsx
+++ b/orchestrator/src/client/pages/SettingsPage.tsx
@@ -75,6 +75,7 @@ const DEFAULT_FORM_VALUES: UpdateSettingsInput = {
chatStyleLanguageMode: null,
chatStyleManualLanguage: null,
rxresumeEmail: "",
+ rxresumeUrl: "",
rxresumePassword: "",
rxresumeApiKey: "",
basicAuthUser: "",
@@ -132,6 +133,7 @@ const NULL_SETTINGS_PAYLOAD: UpdateSettingsInput = {
chatStyleLanguageMode: null,
chatStyleManualLanguage: null,
rxresumeEmail: null,
+ rxresumeUrl: null,
rxresumePassword: null,
rxresumeApiKey: null,
basicAuthUser: null,
@@ -174,6 +176,7 @@ const mapSettingsToForm = (data: AppSettings): UpdateSettingsInput => ({
chatStyleLanguageMode: data.chatStyleLanguageMode.override ?? null,
chatStyleManualLanguage: data.chatStyleManualLanguage.override ?? null,
rxresumeEmail: data.rxresumeEmail ?? "",
+ rxresumeUrl: data.rxresumeUrl ?? "",
rxresumePassword: "",
rxresumeApiKey: "",
basicAuthUser: data.basicAuthUser ?? "",
@@ -715,6 +718,10 @@ export const SettingsPage: React.FC = () => {
envPayload.rxresumeEmail = normalizeString(data.rxresumeEmail);
}
+ if (dirtyFields.rxresumeUrl) {
+ envPayload.rxresumeUrl = normalizeString(data.rxresumeUrl);
+ }
+
if (dirtyFields.ukvisajobsEmail || dirtyFields.ukvisajobsPassword) {
envPayload.ukvisajobsEmail = normalizeString(data.ukvisajobsEmail);
}
diff --git a/orchestrator/src/client/pages/settings/components/ReactiveResumeSection.tsx b/orchestrator/src/client/pages/settings/components/ReactiveResumeSection.tsx
index 400e6c8..8e8f1be 100644
--- a/orchestrator/src/client/pages/settings/components/ReactiveResumeSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/ReactiveResumeSection.tsx
@@ -57,6 +57,7 @@ export const ReactiveResumeSection: React.FC = ({
const rxresumeApiKeyValue =
useWatch({ control, name: "rxresumeApiKey" }) ?? "";
const rxresumeEmailValue = useWatch({ control, name: "rxresumeEmail" }) ?? "";
+ const rxresumeUrlValue = useWatch({ control, name: "rxresumeUrl" }) ?? "";
const rxresumePasswordValue =
useWatch({ control, name: "rxresumePassword" }) ?? "";
const resumeProjectsValue = useWatch({ control, name: "resumeProjects" });
@@ -85,6 +86,12 @@ export const ReactiveResumeSection: React.FC = ({
hasRxResumeAccess={hasRxResumeAccess}
showValidationStatus={Boolean(validationStatuses)}
validationStatuses={validationStatuses}
+ shared={{
+ baseUrl: rxresumeUrlValue,
+ onBaseUrlChange: (value) =>
+ setDirtyTouchedValue("rxresumeUrl", value),
+ baseUrlError: errors.rxresumeUrl?.message as string | undefined,
+ }}
v5={{
apiKey: rxresumeApiKeyValue,
onApiKeyChange: (value) =>
diff --git a/orchestrator/src/server/api/routes/settings.test.ts b/orchestrator/src/server/api/routes/settings.test.ts
index f1a0d88..8faa03a 100644
--- a/orchestrator/src/server/api/routes/settings.test.ts
+++ b/orchestrator/src/server/api/routes/settings.test.ts
@@ -69,6 +69,7 @@ describe.sequential("Settings API routes", () => {
env: {
LLM_API_KEY: "secret-key",
RXRESUME_EMAIL: "resume@example.com",
+ RXRESUME_URL: "https://env.rxresume.example.com",
},
}));
});
@@ -84,6 +85,7 @@ describe.sequential("Settings API routes", () => {
expect(body.data.model.default).toBe("test-model");
expect(Array.isArray(body.data.searchTerms.value)).toBe(true);
expect(body.data.rxresumeEmail).toBe("resume@example.com");
+ expect(body.data.rxresumeUrl).toBe("https://env.rxresume.example.com");
expect(body.data.llmApiKeyHint).toBe("secr");
expect(body.data.basicAuthActive).toBe(false);
});
@@ -124,6 +126,7 @@ describe.sequential("Settings API routes", () => {
body: JSON.stringify({
searchTerms: ["engineer"],
rxresumeEmail: "updated@example.com",
+ rxresumeUrl: "https://resume.example.com",
llmApiKey: "updated-secret",
}),
});
@@ -132,6 +135,7 @@ describe.sequential("Settings API routes", () => {
expect(patchBody.data.searchTerms.value).toEqual(["engineer"]);
expect(patchBody.data.searchTerms.override).toEqual(["engineer"]);
expect(patchBody.data.rxresumeEmail).toBe("updated@example.com");
+ expect(patchBody.data.rxresumeUrl).toBe("https://resume.example.com");
expect(patchBody.data.llmApiKeyHint).toBe("upda");
});
diff --git a/orchestrator/src/server/services/rxresume/index.test.ts b/orchestrator/src/server/services/rxresume/index.test.ts
index 38b2e9b..1758a30 100644
--- a/orchestrator/src/server/services/rxresume/index.test.ts
+++ b/orchestrator/src/server/services/rxresume/index.test.ts
@@ -56,6 +56,7 @@ describe("rxresume adapter", () => {
delete process.env.RXRESUME_API_KEY;
delete process.env.RXRESUME_EMAIL;
delete process.env.RXRESUME_PASSWORD;
+ delete process.env.RXRESUME_URL;
delete process.env.RXRESUME_MODE;
mockSettings({});
});
@@ -157,6 +158,44 @@ describe("rxresume adapter", () => {
expect(result).toEqual({ ok: true, mode: "v4" });
});
+ it("prefers stored rxresumeUrl over environment values", async () => {
+ process.env.RXRESUME_URL = "https://env.rxresume.example.com";
+ mockSettings({
+ rxresumeMode: "v4",
+ rxresumeEmail: "user@example.com",
+ rxresumePassword: "pw",
+ rxresumeUrl: "https://stored.rxresume.example.com",
+ });
+ vi.mocked(RxResumeClient.verifyCredentials).mockResolvedValue({ ok: true });
+
+ await validateCredentials();
+
+ expect(RxResumeClient.verifyCredentials).toHaveBeenCalledWith(
+ "user@example.com",
+ "pw",
+ "https://stored.rxresume.example.com",
+ );
+ });
+
+ it("falls back to the default v4 URL when no env or stored URL is configured", async () => {
+ mockSettings({
+ rxresumeMode: "v4",
+ rxresumeEmail: "user@example.com",
+ rxresumePassword: "pw",
+ });
+ vi.mocked(RxResumeClient.verifyCredentials).mockResolvedValue({ ok: true });
+
+ await validateCredentials({
+ v4: { baseUrl: " " },
+ });
+
+ expect(RxResumeClient.verifyCredentials).toHaveBeenCalledWith(
+ "user@example.com",
+ "pw",
+ "https://v4.rxresu.me",
+ );
+ });
+
it("does not fall back to v4 validation when explicit v5 validation fails", async () => {
mockSettings({
rxresumeMode: "v5",
diff --git a/orchestrator/src/server/services/rxresume/index.ts b/orchestrator/src/server/services/rxresume/index.ts
index 8fb5e27..507299e 100644
--- a/orchestrator/src/server/services/rxresume/index.ts
+++ b/orchestrator/src/server/services/rxresume/index.ts
@@ -172,36 +172,42 @@ async function readConfiguredMode(): Promise {
}
async function readV4Credentials(overrides?: ResolveModeOptions["v4"]) {
- const [storedEmail, storedPassword] = await Promise.all([
+ const [storedEmail, storedPassword, storedBaseUrl] = await Promise.all([
getSetting("rxresumeEmail"),
getSetting("rxresumePassword"),
+ getSetting("rxresumeUrl"),
]);
const email =
overrides?.email?.trim() ||
- process.env.RXRESUME_EMAIL?.trim() ||
storedEmail?.trim() ||
+ process.env.RXRESUME_EMAIL?.trim() ||
"";
const password =
overrides?.password?.trim() ||
- process.env.RXRESUME_PASSWORD?.trim() ||
storedPassword?.trim() ||
+ process.env.RXRESUME_PASSWORD?.trim() ||
"";
const baseUrl =
overrides?.baseUrl?.trim() ||
+ storedBaseUrl?.trim() ||
process.env.RXRESUME_URL?.trim() ||
"https://v4.rxresu.me";
return { email, password, baseUrl, available: Boolean(email && password) };
}
async function readV5Credentials(overrides?: ResolveModeOptions["v5"]) {
- const [storedApiKey] = await Promise.all([getSetting("rxresumeApiKey")]);
+ const [storedApiKey, storedBaseUrl] = await Promise.all([
+ getSetting("rxresumeApiKey"),
+ getSetting("rxresumeUrl"),
+ ]);
const apiKey =
overrides?.apiKey?.trim() ||
- process.env.RXRESUME_API_KEY?.trim() ||
storedApiKey?.trim() ||
+ process.env.RXRESUME_API_KEY?.trim() ||
"";
const baseUrl =
overrides?.baseUrl?.trim() ||
+ storedBaseUrl?.trim() ||
process.env.RXRESUME_URL?.trim() ||
"https://rxresu.me";
return { apiKey, baseUrl, available: Boolean(apiKey) };
diff --git a/shared/src/settings-registry.test.ts b/shared/src/settings-registry.test.ts
index c5ded82..6a2f74d 100644
--- a/shared/src/settings-registry.test.ts
+++ b/shared/src/settings-registry.test.ts
@@ -116,6 +116,10 @@ describe("settingsRegistry helpers", () => {
it("has env-backed v5 api key secret setting", () => {
expect(settingsRegistry.rxresumeApiKey.envKey).toBe("RXRESUME_API_KEY");
});
+
+ it("has env-backed rxresumeUrl string setting", () => {
+ expect(settingsRegistry.rxresumeUrl.envKey).toBe("RXRESUME_URL");
+ });
});
describe("writing-style language settings", () => {
diff --git a/shared/src/settings-registry.ts b/shared/src/settings-registry.ts
index 97342bb..4bafe2d 100644
--- a/shared/src/settings-registry.ts
+++ b/shared/src/settings-registry.ts
@@ -462,6 +462,14 @@ export const settingsRegistry = {
envKey: "RXRESUME_EMAIL",
schema: z.string().trim().max(200),
},
+ rxresumeUrl: {
+ kind: "string" as const,
+ envKey: "RXRESUME_URL",
+ schema: z.preprocess(
+ (value) => (value === "" ? null : value),
+ z.string().trim().url().max(2000).nullable(),
+ ),
+ },
ukvisajobsEmail: {
kind: "string" as const,
envKey: "UKVISAJOBS_EMAIL",
diff --git a/shared/src/settings-schema.test.ts b/shared/src/settings-schema.test.ts
index d7d1a7a..f5677c3 100644
--- a/shared/src/settings-schema.test.ts
+++ b/shared/src/settings-schema.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { updateSettingsSchema } from "./settings-schema";
-describe("updateSettingsSchema language settings", () => {
+describe("updateSettingsSchema", () => {
it("accepts supported language mode and manual language values", () => {
expect(
updateSettingsSchema.parse({
@@ -43,4 +43,32 @@ describe("updateSettingsSchema language settings", () => {
result.error.flatten().fieldErrors.chatStyleManualLanguage,
).toBeDefined();
});
+
+ it("accepts a nullable rxresumeUrl and rejects invalid URLs", () => {
+ expect(
+ updateSettingsSchema.parse({
+ rxresumeUrl: "https://resume.example.com",
+ }),
+ ).toEqual({
+ rxresumeUrl: "https://resume.example.com",
+ });
+
+ expect(
+ updateSettingsSchema.parse({
+ rxresumeUrl: null,
+ }),
+ ).toEqual({
+ rxresumeUrl: null,
+ });
+
+ const result = updateSettingsSchema.safeParse({
+ rxresumeUrl: "not-a-url",
+ });
+
+ expect(result.success).toBe(false);
+ if (result.success) {
+ return;
+ }
+ expect(result.error.flatten().fieldErrors.rxresumeUrl).toBeDefined();
+ });
});
diff --git a/shared/src/testing/factories.ts b/shared/src/testing/factories.ts
index 459b847..900facd 100644
--- a/shared/src/testing/factories.ts
+++ b/shared/src/testing/factories.ts
@@ -201,6 +201,7 @@ export const createAppSettings = (
llmApiKeyHint: null,
rxresumeApiKeyHint: null,
rxresumeEmail: null,
+ rxresumeUrl: null,
rxresumePasswordHint: null,
basicAuthUser: null,
basicAuthPasswordHint: null,
diff --git a/shared/src/types/settings.ts b/shared/src/types/settings.ts
index 669baec..a884a14 100644
--- a/shared/src/types/settings.ts
+++ b/shared/src/types/settings.ts
@@ -183,6 +183,7 @@ export interface AppSettings {
rxresumeBaseResumeIdV4: string | null;
rxresumeBaseResumeIdV5: string | null;
rxresumeEmail: string | null;
+ rxresumeUrl: string | null;
ukvisajobsEmail: string | null;
adzunaAppId: string | null;
basicAuthUser: string | null;