Speed Up Candidate Outreach with the OnlineJobs.ph Application Assistant

7/27/2026
Speed Up Candidate Outreach with the OnlineJobs.ph Application Assistant

Speed Up Job Applications with the OnlineJobs.ph Application Assistant

Applying for opportunities on OnlineJobs.ph often follows the same repetitive process: open a job posting, identify the important requirements, review the employer’s instructions, prepare a tailored application message, proofread it, and submit it.

Repeating those steps across multiple job listings can consume significant time and attention. It can also lead to generic messages, overlooked instructions, incorrect details, and copy-and-paste mistakes.

The OnlineJobs.ph Application Assistant , developed by Mark Neil Cordero , is a Chrome extension designed to simplify this workflow. It helps users review job information and prepare a relevant application draft while keeping the final message under their control.

The Business Problem: Repetitive Application Work

A serious job application requires more than sending the same introduction to every employer. Before applying, a candidate may need to identify:

  • The job title and primary responsibilities
  • The required technical and professional skills
  • The employer’s application instructions
  • The expected schedule, compensation, or availability
  • Questions that must be answered in the application
  • Keywords, codes, or subject-line requirements included in the posting

The candidate must then connect those requirements to relevant skills, experience, portfolio projects, and availability. When many listings are being reviewed, this process becomes a bottleneck.

The challenge is not simply writing faster. It is preparing a message that remains accurate, specific to the role, and easy to review before submission.

Why Generic Templates Are Often Insufficient

Reusable templates are useful for consistent introductions and contact information, but a fixed template cannot fully understand the context of an individual job posting.

A generic macro or text-expansion tool usually cannot:

  • Identify the most important requirements in the current listing
  • Recognize employer-specific application instructions
  • Connect the role with the applicant’s relevant skills or projects
  • Adjust the wording according to the position
  • Generate a draft that can be reviewed before submission

Rigid automation can also create risk. A message may mention the wrong technology, company, role, or experience when information from a previous application is accidentally reused.

A better approach is assisted drafting : automate the repetitive preparation work while requiring the user to review, edit, and approve the final message.

How a Custom Chrome Extension Solves the Problem

A purpose-built Chrome extension can work directly within the browser workflow. Instead of repeatedly moving between the job board, a text editor, an AI chat interface, and a portfolio page, the extension can coordinate the process from the active job listing.

A practical implementation can:

  • Read relevant job information from the current page
  • Organize the extracted information into structured data
  • Send the necessary context to an approved AI endpoint
  • Generate a concise application draft
  • Display the result in an editable extension interface
  • Allow the user to revise and copy the final message

This workflow reduces repetitive typing without removing human judgment. The extension assists with preparation, but the applicant remains responsible for checking factual accuracy, answering employer questions, and deciding what to submit.

Example End-to-End Workflow

  1. Open a job listing. The user reviews an opportunity on OnlineJobs.ph.
  2. Extract the job details. A content script reads the visible title, description, requirements, and other relevant fields.
  3. Validate the extracted information. The extension checks whether enough content was found before requesting a draft.
  4. Prepare the AI request. The background service worker creates a structured prompt using the job details and the applicant’s approved profile information.
  5. Generate the draft. The request is sent to a configured service, such as a local Ollama endpoint or another authorized API.
  6. Return the response. The generated message is sent back to the extension interface.
  7. Review and edit. The user checks the draft, corrects any assumptions, and adds information that automation cannot verify.
  8. Copy and submit manually. The approved message is placed into the application form only after review.

This approach is safer than automatically submitting applications because it preserves an explicit review step.

Implementation Considerations

1. Resilient DOM Extraction

Content scripts depend on the structure of the website. If OnlineJobs.ph changes its HTML markup, selectors that previously worked may stop finding the correct elements.

The extraction logic should therefore:

  • Use multiple fallback selectors for important fields
  • Prefer semantic attributes and stable containers when available
  • Check whether extracted values are empty or unexpectedly short
  • Return a clear error instead of silently producing an incomplete draft
  • Keep selectors isolated in one configuration object for easier updates

The selectors in the following examples are illustrative. They must be inspected and updated according to the website’s current structure.

2. Privacy and Data Minimization

The extension should collect only the information needed to prepare the draft. Unrelated page content, private messages, credentials, and unnecessary personal information should not be included in the request.

When using a remote API, communication should use HTTPS. API keys should not be hard-coded into a publicly distributed extension because users can inspect an extension’s packaged source files.

Safer approaches include:

  • Using a controlled backend that keeps provider credentials on the server
  • Allowing users to provide and manage their own API configuration
  • Using a local AI service such as Ollama when appropriate
  • Documenting what information is collected, stored, and transmitted

3. Manifest V3 Architecture

A modern Chrome extension should use Manifest V3. A typical architecture includes:

  • A content script for reading the active job page
  • A service worker for coordinating requests and extension events
  • A popup or side-panel interface for generating and editing drafts
  • Chrome storage for approved settings, templates, and profile information

Permissions should remain as limited as possible. Broad host access should not be requested when the extension only needs access to a specific website and a configured AI endpoint.

4. Error Handling

The interface should explain what went wrong and what the user can do next. Useful error states include:

  • The current tab is not a supported OnlineJobs.ph job page
  • The posting could not be extracted because the page structure changed
  • The AI endpoint is unavailable or incorrectly configured
  • The request exceeded its time limit
  • The service returned an invalid or empty response
  • The API rejected the request because of authentication or usage limits

Errors should not erase the user’s existing draft. Where practical, the extension should allow the user to retry the request or continue editing manually.

5. Rate Limiting and Request Control

Repeated clicks can create duplicate AI requests. The interface should temporarily disable the generate button while a request is active and apply a reasonable timeout.

If a remote provider enforces usage limits, the extension should handle HTTP status codes such as 429 and display a clear message. Automatic retries should be limited and delayed so they do not create additional unnecessary requests.

6. Prompt and Extension Versioning

The extension code and the drafting prompt should both be versioned. A prompt change can affect the structure and tone of generated messages even when the extension’s interface remains unchanged.

Maintaining versioned configuration makes it easier to:

  • Track changes in drafting behavior
  • Roll back a problematic prompt
  • Migrate saved settings safely
  • Identify which release introduced an extraction issue
  • Document updates in Chrome Web Store release notes

Practical Content-Script Example

The following simplified content script extracts job information, validates the result, and responds to requests from the extension interface.

// content-script.js

(() => {
  const SELECTORS = {
    title: [
      'h1.job-title',
      'h1',
      '[data-testid="job-title"]'
    ],
    budget: [
      '.budget .value',
      '.salary',
      '[data-testid="job-budget"]'
    ],
    deadline: [
      '.deadline .value',
      '[data-testid="job-deadline"]'
    ],
    description: [
      '.job-description',
      '[data-testid="job-description"]',
      'main article'
    ],
    skills: [
      '.skills li',
      '[data-testid="job-skills"] li'
    ]
  };

  function getFirstText(selectors) {
    for (const selector of selectors) {
      const element = document.querySelector(selector);
      const text = element?.textContent?.trim();

      if (text) {
        return text;
      }
    }

    return '';
  }

  function getListText(selectors) {
    for (const selector of selectors) {
      const values = Array.from(document.querySelectorAll(selector))
        .map((element) => element.textContent?.trim())
        .filter(Boolean);

      if (values.length > 0) {
        return [...new Set(values)];
      }
    }

    return [];
  }

  function extractJobData() {
    return {
      url: window.location.href,
      title: getFirstText(SELECTORS.title),
      budget: getFirstText(SELECTORS.budget),
      deadline: getFirstText(SELECTORS.deadline),
      skills: getListText(SELECTORS.skills),
      description: getFirstText(SELECTORS.description)
    };
  }

  function validateJobData(jobData) {
    if (!jobData.title && !jobData.description) {
      throw new Error(
        'The extension could not identify the job title or description.'
      );
    }

    if (jobData.description.length < 40) {
      throw new Error(
        'The extracted job description appears incomplete.'
      );
    }
  }

  chrome.runtime.onMessage.addListener(
    (message, _sender, sendResponse) => {
      if (message?.action !== 'extractJob') {
        return false;
      }

      try {
        const jobData = extractJobData();
        validateJobData(jobData);

        sendResponse({
          ok: true,
          data: jobData
        });
      } catch (error) {
        sendResponse({
          ok: false,
          error:
            error instanceof Error
              ? error.message
              : 'The job information could not be extracted.'
        });
      }

      return false;
    }
  );
})();

This design waits for an explicit request from the popup instead of automatically sending page content when the page loads. That gives the user greater control over when extraction and AI processing occur.

AI Request and Response Logic

The service worker can receive the structured job data, prepare a constrained prompt, call the configured AI endpoint, and return the generated draft.

// service-worker.js

const OLLAMA_ENDPOINT = 'http://127.0.0.1:11434/api/generate';
const MODEL_NAME = 'phi3:mini';
const REQUEST_TIMEOUT_MS = 45000;

function buildPrompt(jobData, applicantProfile) {
  const skills = Array.isArray(jobData.skills)
    ? jobData.skills.join(', ')
    : '';

  return `
Write a concise and professional job application message.

Rules:
- Use only facts provided in the job data and applicant profile.
- Do not invent experience, results, clients, education, or skills.
- Address the employer's main requirements.
- Keep the message specific to the role.
- Do not claim that the application has already been submitted.
- Return only the application message.

JOB DATA
Title: ${jobData.title || 'Not specified'}
Budget: ${jobData.budget || 'Not specified'}
Deadline: ${jobData.deadline || 'Not specified'}
Skills: ${skills || 'Not specified'}
Description:
${jobData.description || 'Not specified'}

APPLICANT PROFILE
Name: ${applicantProfile.name || 'Not specified'}
Professional summary:
${applicantProfile.summary || 'Not specified'}
Relevant skills:
${applicantProfile.skills || 'Not specified'}
Relevant projects:
${applicantProfile.projects || 'Not specified'}
Portfolio:
${applicantProfile.portfolioUrl || 'Not specified'}
`.trim();
}

async function requestDraft(jobData, applicantProfile) {
  const controller = new AbortController();
  const timeoutId = setTimeout(
    () => controller.abort(),
    REQUEST_TIMEOUT_MS
  );

  try {
    const response = await fetch(OLLAMA_ENDPOINT, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: MODEL_NAME,
        prompt: buildPrompt(jobData, applicantProfile),
        stream: false
      }),
      signal: controller.signal
    });

    if (response.status === 429) {
      throw new Error(
        'The AI service is receiving too many requests. Try again later.'
      );
    }

    if (!response.ok) {
      throw new Error(
        `The AI service returned HTTP ${response.status}.`
      );
    }

    const result = await response.json();
    const draft = result?.response?.trim();

    if (!draft) {
      throw new Error('The AI service returned an empty draft.');
    }

    return draft;
  } catch (error) {
    if (error instanceof DOMException && error.name === 'AbortError') {
      throw new Error('The AI request timed out.');
    }

    throw error;
  } finally {
    clearTimeout(timeoutId);
  }
}

chrome.runtime.onMessage.addListener(
  (message, _sender, sendResponse) => {
    if (message?.action !== 'generateDraft') {
      return false;
    }

    (async () => {
      try {
        const { applicantProfile = {} } =
          await chrome.storage.local.get('applicantProfile');

        const draft = await requestDraft(
          message.jobData,
          applicantProfile
        );

        sendResponse({
          ok: true,
          draft
        });
      } catch (error) {
        sendResponse({
          ok: false,
          error:
            error instanceof Error
              ? error.message
              : 'The draft could not be generated.'
        });
      }
    })();

    return true;
  }
);

A local Ollama endpoint may not be directly reachable from every installation without the correct host permissions, local service configuration, and network settings. A production extension should make the provider configurable and provide clear setup instructions.

Popup Interface Handling

The popup coordinates extraction and generation. It should preserve the generated text in an editable field and clearly display loading or error states.

// popup.js

const generateButton = document.querySelector('#generate-button');
const draftField = document.querySelector('#draft');
const statusElement = document.querySelector('#status');

function setLoading(isLoading) {
  generateButton.disabled = isLoading;
  generateButton.textContent = isLoading
    ? 'Generating...'
    : 'Generate Draft';
}

function showStatus(message, isError = false) {
  statusElement.textContent = message;
  statusElement.setAttribute(
    'role',
    isError ? 'alert' : 'status'
  );
}

async function getActiveTab() {
  const [tab] = await chrome.tabs.query({
    active: true,
    currentWindow: true
  });

  if (!tab?.id) {
    throw new Error('No active browser tab was found.');
  }

  return tab;
}

function sendTabMessage(tabId, message) {
  return new Promise((resolve, reject) => {
    chrome.tabs.sendMessage(tabId, message, (response) => {
      if (chrome.runtime.lastError) {
        reject(
          new Error(
            'Open a supported OnlineJobs.ph job page and try again.'
          )
        );
        return;
      }

      resolve(response);
    });
  });
}

function sendRuntimeMessage(message) {
  return new Promise((resolve, reject) => {
    chrome.runtime.sendMessage(message, (response) => {
      if (chrome.runtime.lastError) {
        reject(new Error(chrome.runtime.lastError.message));
        return;
      }

      resolve(response);
    });
  });
}

generateButton.addEventListener('click', async () => {
  setLoading(true);
  showStatus('Reading the job posting...');

  try {
    const tab = await getActiveTab();

    const extractionResult = await sendTabMessage(tab.id, {
      action: 'extractJob'
    });

    if (!extractionResult?.ok) {
      throw new Error(
        extractionResult?.error ||
        'The job posting could not be extracted.'
      );
    }

    showStatus('Preparing your application draft...');

    const draftResult = await sendRuntimeMessage({
      action: 'generateDraft',
      jobData: extractionResult.data
    });

    if (!draftResult?.ok) {
      throw new Error(
        draftResult?.error ||
        'The application draft could not be generated.'
      );
    }

    draftField.value = draftResult.draft;
    showStatus(
      'Draft generated. Review every detail before submitting.'
    );
  } catch (error) {
    showStatus(
      error instanceof Error
        ? error.message
        : 'An unexpected error occurred.',
      true
    );
  } finally {
    setLoading(false);
  }
});

Basic Manifest Configuration

The manifest connects the content script, service worker, popup, permissions, and supported domains.

{
  "manifest_version": 3,
  "name": "OnlineJobs.ph Application Assistant",
  "version": "1.0.0",
  "description": "Assists users in preparing editable application drafts from OnlineJobs.ph job postings.",
  "permissions": [
    "activeTab",
    "storage"
  ],
  "host_permissions": [
    "https://www.onlinejobs.ph/*",
    "http://127.0.0.1:11434/*"
  ],
  "background": {
    "service_worker": "service-worker.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "matches": [
        "https://www.onlinejobs.ph/*"
      ],
      "js": [
        "content-script.js"
      ]
    }
  ]
}

The final permissions depend on the extension’s exact implementation. Any permission that is not required should be removed before release.

Building and Testing the Extension

1. Create the Project Structure

onlinejobs-application-assistant/
├── manifest.json
├── content-script.js
├── service-worker.js
├── popup.html
├── popup.js
├── popup.css
└── icons/

2. Load the Extension Locally

  1. Open chrome://extensions in Google Chrome.
  2. Enable Developer mode .
  3. Select Load unpacked .
  4. Choose the extension’s project folder.
  5. Open a supported job page and test the extraction process.

3. Test the Complete Workflow

Testing should cover both successful and unsuccessful conditions:

  • A normal job posting with complete information
  • A posting with missing budget, skills, or deadline fields
  • A page that is not a job listing
  • An unavailable or incorrectly configured AI endpoint
  • A slow request that reaches the timeout
  • An empty or malformed AI response
  • Repeated clicks on the generate button
  • A modified page layout that breaks the primary selectors

The generated message should also be reviewed for unsupported claims. Prompt instructions reduce hallucinations, but they do not replace user verification.

4. Prepare a Production Build

Before distribution:

  • Remove debugging logs and unused files
  • Confirm that no private API keys are included
  • Review all requested permissions
  • Add appropriately sized extension icons
  • Update the version number
  • Document configuration requirements
  • Test the packaged extension in a clean browser profile

Publishing Through the Chrome Web Store

A production release typically requires the developer to package the extension, create a Chrome Web Store developer listing, upload the ZIP archive, provide listing information, and submit the extension for review.

The listing should accurately explain:

  • What the extension does
  • Which pages it accesses
  • Why each permission is required
  • Whether job information is sent to an external or local AI service
  • How users configure the extension
  • How users can request support or report a problem

Any required privacy disclosures should match the extension’s actual behavior. The store description should not claim features that are unavailable in the submitted release.

Maintaining the Extension After Release

Publishing is not the end of the development process. A browser extension that depends on a third-party website needs ongoing maintenance.

A practical maintenance process includes:

  • Monitoring reports of failed extraction
  • Testing after significant OnlineJobs.ph interface changes
  • Updating fallback selectors when necessary
  • Reviewing changes to AI provider APIs
  • Maintaining release notes and semantic version numbers
  • Migrating stored settings when the data structure changes
  • Rechecking permissions before every release

Optional internal diagnostics can record which extraction field failed without collecting the job content itself. Any diagnostic collection should be transparent and privacy-conscious.

Where Mark Neil Cordero Can Help

Mark Neil Cordero is a Full Stack Developer and Chrome Extension Developer with experience building web applications, business systems, browser-based productivity tools, automation workflows, API integrations, and AI-assisted applications.

For a project like the OnlineJobs.ph Application Assistant, his relevant work can include:

  • Designing a Manifest V3 Chrome extension architecture
  • Building content scripts that interact with website pages
  • Creating popup, settings, and workflow interfaces
  • Integrating local or remote AI services
  • Developing secure backend API proxies when required
  • Managing extension storage and user configuration
  • Adding editable templates and reusable applicant profiles
  • Handling errors, timeouts, rate limits, and changing DOM structures
  • Preparing the extension for testing and Chrome Web Store submission
  • Extending the workflow with approved CRM or business-system integrations

The objective is not to automate every decision. It is to identify the repetitive parts of a browser workflow and build a focused tool that makes them faster, more consistent, and easier to manage.

Practical Implementation Checklist

  • Define exactly which job information the extension needs
  • Inspect the current page structure and document fallback selectors
  • Choose between a local AI model, user-provided API access, or a secure backend
  • Store only approved applicant profile information
  • Require manual review before an application is submitted
  • Add visible loading, timeout, and error states
  • Prevent duplicate requests and handle provider limits
  • Test incomplete listings and website layout changes
  • Version the extension, prompts, and saved settings
  • Review permissions, privacy disclosures, and store-listing details

Final Thoughts

Job applications require judgment, accuracy, and personalization, but not every step needs to be performed manually. A focused Chrome extension can extract relevant information, prepare an editable AI-assisted draft, and reduce repetitive work without removing the applicant’s control over the final message.

The OnlineJobs.ph Application Assistant is available through the Chrome Web Store. You can view the project and try it as part of your application workflow.

Companies that need a related browser tool—such as a custom Chrome extension, AI-assisted workflow, internal automation system, editable template solution, or API integration—can contact Mark Neil Cordero to discuss the specific process, technical requirements, and appropriate implementation.

Share on Facebook