根据用户简历自动匹配合适职位,利用 resumex.dev API、网络搜索和浏览器自动化投递。请在需要时使用此技能。
This skill connects to the user's resumex.dev account, reads their resume data, matches them to relevant jobs via web search, presents an approval list, then automatically applies to approved jobs using browser automation — filling forms, answering screening questions, and submitting applications. All applications are logged to the resumex.dev job tracker.
Architecture: ResumeX stores resume data and the job tracker. OpenClaw's built-in AI does all the thinking and text generation (cover letters, screening answers, scoring). Web search finds jobs. Browser tool fills and submits applications.
user_preferences.jsonremembers extra info between sessions.
No third-party AI API keys required. This skill uses only OpenClaw's built-in LLM for all AI tasks (cover letter drafting, screening question answers, job scoring). The only external API key needed is
RESUMEX_API_KEY.
VariableRequiredDescriptionRESUMEX_API_KEY✅ RequiredAPI key from resumex.dev → Dashboard → Resumex APIJOB_SEARCH_LOCATIONOptionalOverride city/country for job searchJOB_TYPEOptionalfull-time | part-time | contract | internship``REMOTE_ONLYOptionaltrue | false (default: false)MAX_APPLICATIONSOptionalMax jobs to apply per session (default: 5)
How to set RESUMEX_API_KEY in OpenClaw:
RESUMEX_API_KEY with the copied key valueNo other keys are needed. There is no Anthropic key, no OpenAI key, no other third-party service.
Read this before using the skill.
DataSent ToWhyResume data (API read)resumex.devTo fetch your resume for job matchingApplication logs (company, role, URL, status)resumex.devTo track your job applicationsYour name, email, phone, LinkedInJob application websitesTo fill application formsCover letter (generated text)Job application websitesSubmitted as part of each application
Data sent to resumex.dev is governed by the resumex.dev Privacy Policy.
DataLocationWhat It Containsdata/user_preferences.jsonSkill directory onlySalary expectation, visa status, notice period, address, gender, date of birth, ethnicity, veteran status, disability status, screening question answers
⚠️
user_preferences.jsonmay contain sensitive personal data including date of birth, gender, ethnicity, veteran status, and disability status (only if you choose to save these when prompted during a form fill). This file is stored locally only and is never sent to resumex.dev or any other server. Review and restrict its filesystem permissions if needed:bash
chmod 600 data/user_preferences.jsonTo clear all saved preferences at any time:
bash
python3 scripts/manage_preferences.py reset
The following fields are only saved if you explicitly provide them when the agent encounters a form field that requires them. You can decline to answer, skip the field, or delete a saved value at any time:
gender — only for diversity/EEO formsethnicity — only for diversity/EEO forms (optional, you may leave blank)veteran_status — only for U.S. government/contractor compliance formsdisability_status — only for compliance forms (optional)date_of_birth — only for forms that legally require itThe agent will always tell you which form requires a sensitive field before asking for the value.
The agent NEVER submits an application without your explicit approval. Step 6 of the workflow
always presents a formatted approval list and waits for your response before any browser
interaction begins. If you are testing, set MAX_APPLICATIONS=1 in your environment.
text
1. Fetch resume data from resumex.dev API
2. Load saved user preferences (salary, visa, screening answers)
3. Build a job-match profile (skills, roles, seniority, preferences)
4. Search the web for matching jobs (3–5 query permutations)
5. Score & rank each job against the resume (0–100)
6. ⛔ APPROVAL GATE — Present formatted list → wait for user selection
7. For each approved job:
a. Generate a tailored cover letter (via OpenClaw's built-in AI)
b. Navigate to application page via browser
c. Fill form fields using resume data + preferences
d. If a required field is unknown → ask the user → save to preferences
e. Submit the application
f. Log to resumex.dev job tracker
8. Present final summary with statuses
Use the agent endpoint. All calls require Authorization: Bearer $RESUMEX_API_KEY.
bash
# Fetch full resume data (GET /api/v1/agent — the correct endpoint)
curl -s -X GET "https://resumex.dev/api/v1/agent" \
-H "Authorization: Bearer $RESUMEX_API_KEY" \
-H "Content-Type: application/json"
Note: The endpoint is
/api/v1/agent(NOT/api/v1/agent/resume, which is deprecated). The helper scripts handle retries with exponential backoff automatically. Install dependencies first:pip3 install -r requirements.txt
Or via the helper script:
bash
# Full resume JSON
python3 scripts/fetch_resume.py
# Extract a specific field for form filling
python3 scripts/fetch_resume.py --field email
python3 scripts/fetch_resume.py --json-path profile.phone
Expected response shape:
json
{
"success": true,
"data": {
"activeResumeId": "...",
"resumes": [{
"id": "...",
"data": {
"profile": {
"fullName": "...", "email": "...", "phone": "...",
"location": "...", "summary": "...",
"linkedin": "...", "github": "...", "website": "..."
},
"skills": [{"category": "...", "skills": ["...", "..."]}],
"experience": [
{
"role": "...", "company": "...", "location": "...",
"startDate": "...", "endDate": "...", "description": "..."
}
],
"education": [{"degree": "...", "institution": "...", "endDate": "...", "score": "..."}],
"projects": [{"name": "...", "description": "...", "tags": ["..."]}],
"achievements": [{"title": "...", "year": "..."}]
}
}]
}
}
Parse the active resume:
text
workspace = response.data
activeResume = workspace.resumes.find(r => r.id === workspace.activeResumeId)
resumeData = activeResume.data
Error handling:
HTTP CodeCauseFix401``RESUMEX_API_KEY is missing or invalidGo to resumex.dev → Dashboard → Resumex API → generate a new key404Resume not created yetGo to resumex.dev → create and publish your resume429Rate limitedWait 10 seconds, retry once
Check for previously saved preferences that supplement the resume data:
bash
python3 scripts/manage_preferences.py list
This returns any saved answers like salary expectation, visa status, notice period, etc.
If user_preferences.json doesn't exist yet, that's fine — it will be created when the
user is first asked for missing information.
Preference fields to look for:
salary_expectation — e.g. "8-12 LPA" or "$80,000-$100,000"currency — e.g. "INR" or "USD"notice_period — e.g. "30 days" or "Immediate"visa_status — e.g. "No visa required (Indian citizen)"work_authorization — e.g. "Authorized to work in India"willing_to_relocate — true/falsepreferred_work_type — "remote" | "hybrid" | "onsite"screening_answers — dict of previously answered screening questionsFrom the resume JSON and user preferences, extract and infer:
FieldHow to DeriveTarget rolesLatest experience[0].role + adjacent titles (e.g. "Software Engineer" → "Backend Developer", "Full Stack Developer")Key skillsTop 5–8 from flattened skills[].skills arrays + tech stack from experience[].descriptionSeniorityYears of experience calculated from earliest startDate to todayLocationprofile.location (override with JOB_SEARCH_LOCATION env var if set)Job typeJOB_TYPE env var or preferred_work_type from preferences (default: full-time)RemoteREMOTE_ONLY env var (default: false)IndustryInfer from company names / job descriptions in experience
Example derived profile:
text
Roles: Software Engineer, Backend Developer, Full Stack Developer
Skills: Python, Django, React, PostgreSQL, Docker, AWS
Seniority: Mid-level (3 years)
Location: Pune, India
Type: Full-time
Remote: No preference
Salary: 8-12 LPA (from preferences)
Use web_search to find real, current job postings. Run 3–5 targeted searches using different query permutations to maximize coverage.
Query templates:
text
"{role}" "{top_skill}" jobs "{location}" site:linkedin.com OR site:naukri.com OR site:indeed.com
"{role}" "{top_skill}" "{second_skill}" hiring 2026
"{role}" remote jobs "{top_skill}" "{seniority}"
"{role}" "{top_skill}" jobs "{location}" "apply now" site:wellfound.com OR site:internshala.com
See references/job_boards.md for complete query patterns per board.
For each search result URL, use web_fetch to extract:
form | easy-apply | email | redirectAim to collect 10–20 raw job postings before scoring.
Score each job 0–100 against the resume profile:
FactorMax PointsSkill overlap (required skills matched)40Role title match20Seniority match15Location / remote match15Industry familiarity10
Formula:
text
score = (skills_matched / skills_required) * 40
+ role_title_match * 20 # 20 if exact, 10 if adjacent, 0 if unrelated
+ seniority_match * 15 # 15 if exact, 8 if ±1 level, 0 if 2+ off
+ location_match * 15 # 15 if match, 8 if remote, 0 if mismatch
+ industry_match * 10 # 10 if same industry, 5 if adjacent
Present the top 10 matches in a formatted table. The user MUST approve before any applications are submitted. Never auto-apply without explicit approval. Never skip this step.
Format:
text
🎯 Job Match Results for [Name]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Score Company Role Location Apply Method
── ───── ─────── ──── ──────── ────────────
1 92 Acme Corp Software Engineer Pune (On-site) 🤖 Auto-apply
2 87 TechStartup Backend Developer Remote 🤖 Auto-apply
3 81 MegaCorp India Full Stack Engineer Mumbai 🤖 Auto-apply
4 76 DevShop Python Developer Pune (Hybrid) 🤖 Auto-apply
5 73 CloudCo API Engineer Remote 🤖 Auto-apply
6 70 DataInc Backend Engineer Bangalore 🔗 Manual (LinkedIn)
7 68 StartupXYZ Software Developer Remote 🤖 Auto-apply
8 65 BigTech Junior SWE Hyderabad 🤖 Auto-apply
9 62 ConsultFirm Technical Consultant Pune 📧 Email apply
10 58 SmallCo Full Stack Developer Remote 🤖 Auto-apply
🤖 = Agent will fill and submit the application automatically
🔗 = LinkedIn — agent will open the page, you submit manually
📧 = Email — agent will draft the email, you review and send
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Which jobs would you like to apply to?
Options: "all", "1,3,5", "1-5", "none", or "1-5 except 3"
Apply method classification:
🤖 Auto-apply — Standard form-based application. Agent fills and submits.🔗 Manual (LinkedIn) — LinkedIn Easy Apply. Agent navigates to the page but user must submit.
(LinkedIn automated submission is disabled by default due to ToS concerns.)📧 Email apply — Agent drafts the application email for user review.🔗 Manual (redirect) — Redirects to external ATS. Agent navigates, user may need to complete.Wait for the user to respond with their selection before proceeding.
For each job the user approved, execute the following sub-steps:
No external AI API is used. Cover letters are generated by OpenClaw's own LLM.
The draft_cover_letter.py script reads resume data and job details, then outputs a structured
prompt. OpenClaw's agent uses that prompt with its built-in AI to generate the cover letter.
bash
python3 scripts/draft_cover_letter.py \
--resume /tmp/resume.json \
--job_title "Software Engineer" \
--company "Acme Corp" \
--job_description "We are looking for..." \
--output /tmp/cover_letter_acme.txt
The script outputs the generation prompt to stdout. The agent then:
--output path if specifiedCover letter structure:
Keep it under 200 words. Professional but human tone.
Use the browser tool to navigate to the job's application URL:
text
browser: navigate to "{job_url}"
Wait for the page to load. Take a screenshot to confirm you're on the right page.
Analyze the page to identify the application form. Look for:
<input>, <textarea>, <select>)*, required attribute)See references/form_field_mappings.md for mapping form labels to resume data.
For each form field, use this priority order to find the value:
text
1. Resume data (from ResumeX API) → profile.email, profile.phone, etc.
2. User preferences (from preferences.json) → salary_expectation, visa_status, etc.
3. Derived data (calculated by agent) → years of experience, full name split, etc.
4. Ask the user (last resort) → save answer to preferences for reuse
Common field mappings (see references/form_field_mappings.md for full list):
Form LabelSourceJSON PathFirst NameResumeprofile.fullName (split, take first)Last NameResumeprofile.fullName (split, take last)EmailResumeprofile.emailPhoneResumeprofile.phoneLinkedIn URLResumeprofile.linkedinGitHub URLResumeprofile.githubWebsiteResumeprofile.websiteCurrent LocationResumeprofile.locationCurrent CompanyResumeexperience[0].companyCurrent TitleResumeexperience[0].roleYears of ExperienceDerivedCalculate from earliest startDateSalary ExpectationPreferencessalary_expectationNotice PeriodPreferencesnotice_periodCover LetterGeneratedFrom Step 7a
Browser fill commands:
text
browser: click on the "First Name" input field
browser: type "{first_name}"
browser: click on the "Email" input field
browser: type "{email}"
...
browser: click on the "Cover Letter" textarea
browser: type "{cover_letter_text}"
If the form has a resume/CV upload field:
If a form field requires information not in the resume or saved preferences:
bash
python3 scripts/manage_preferences.py set "[field_key]" "[user_answer]"
When asking for sensitive fields (gender, ethnicity, DOB, veteran/disability status), always mention which company's form requires it and that the answer will be saved locally. The user may decline — if so, skip the field or leave it blank if optional.
Don't ask again for the same field type. Once "salary expectation" is saved, use it for all future applications unless the user explicitly changes it.
Many application forms include screening questions. For each question:
user_preferences.screening_answers for a saved answer (fuzzy match on question text)bash
python3 scripts/manage_preferences.py set-screening "authorized_to_work" "Yes"
Common screening questions and strategies:
visa_status / work_authorizationvisa_statussalary_expectationnotice_periodwilling_to_relocateSee references/screening_questions.md for the full list.
After all fields are filled:
After submission (or attempted submission), log the application:
bash
python3 scripts/log_application.py \
--company "Acme Corp" \
--role "Software Engineer" \
--url "https://careers.acme.com/apply/12345" \
--status "applied" \
--method "auto-applied" \
--score 92 \
--notes "Auto-applied via job applier skill."
Use --dry-run to test without calling the API:
bash
python3 scripts/log_application.py \
--company "Test" --role "Test" --url "https://example.com" --dry-run
The script automatically tries multiple endpoints ( /api/v1/agent/jobs, then /api/v1/agent/logs)
with retry logic and exponential backoff.
Or via curl:
bash
curl -s -X POST "https://resumex.dev/api/v1/agent/jobs" \
-H "Authorization: Bearer $RESUMEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"company": "Acme Corp",
"position": "Software Engineer",
"url": "https://careers.acme.com/apply/12345",
"status": "applied",
"appliedDate": "2026-04-14",
"notes": "Auto-applied via Auto Job Applier skill. Match score: 92/100. Method: auto-applied."
}'
After all approved applications are processed, show a clean summary:
text
✅ Application Summary for [Name]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Company Role Score Status
1 Acme Corp Software Engineer 92/100 ✅ Applied (auto)
3 MegaCorp India Full Stack Engineer 81/100 ✅ Applied (auto)
5 CloudCo API Engineer 73/100 ✅ Applied (auto)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Results: 3 applied ✅ | 0 failed ❌ | 0 manual 🔗
📝 Notes:
• All applications logged to your resumex.dev tracker
• Cover letters generated using OpenClaw's built-in AI
• Saved preferences: salary_expectation, notice_period (for future use)
💡 Tip: Re-run this skill weekly for fresh job listings!
If any applications had issues:
text
⚠️ Issues:
• CloudCo (API Engineer) — Resume upload required. Open the application and upload manually:
https://careers.cloudco.com/apply/67890
All HTTP scripts use the shared http_client.py module which provides automatic retry
with exponential backoff. Transient errors (429, 5xx, network timeouts) are retried up
to 3 times before failing. The Retry-After header is respected for rate limits.
ErrorCategoryResponse401 UnauthorizedAuthAsk user to check RESUMEX_API_KEY in OpenClaw environment403 ForbiddenAuthAPI key lacks permission — regenerate at resumex.dev404 Not FoundNot FoundResume may not be set up, or endpoint has changed429 Rate LimitedRate LimitAuto-retried up to 3×, respects Retry-After header5xx Server ErrorServerAuto-retried with exponential backoff (1s, 2s, 4s)Network timeoutNetworkAuto-retried once, then fails with clear messageNo jobs foundSearchBroaden search: remove location filter, try adjacent rolesWeb search blockedSearchTry alternate job boards (Naukri, Internshala, Wellfound, etc.)Browser can't load pageBrowserNote the job as "manual apply" and provide the linkForm field type unknownFormAsk the user what to enter, save to preferencesSubmit button not foundFormTake screenshot, ask user to review the pageApplication page requires loginBrowserInform user to log in first, then retryCAPTCHA detectedBrowserSkip this application, mark as "manual", provide linkMulti-step form timeoutBrowserSave progress screenshot, note which step failed
data/user_preferences.json and never transmitted to any server.FilePurposeSKILL.mdThis file — main instructions for the agentscripts/http_client.pyShared HTTP client with retries, backoff, and error classificationscripts/fetch_resume.pyFetches and parses resume from resumex.dev (supports --field extraction)scripts/search_jobs.pyConstructs search queries and parses job posting resultsscripts/fill_application.pyMaps form fields to resume data, outputs browser instructionsscripts/manage_preferences.pyCRUD for user_preferences.json (salary, visa, screening answers)scripts/draft_cover_letter.pyBuilds cover letter prompt for OpenClaw's built-in AI (no external API)scripts/log_application.pyLogs a job application to resumex.dev tracker (with --dry-run support)requirements.txtPython dependencies ( requests>=2.28.0)references/job_boards.mdJob board search patterns, form selectors, browser notesreferences/form_field_mappings.mdMaps form field labels → resume JSON pathsreferences/screening_questions.mdCommon screening questions and handling strategiesdata/user_preferences.jsonPersistent local storage for user answers (auto-created at runtime)