{
  "openapi": "3.0.1",
  "info": {
    "title": "AI Job Match + LinkedIn Jobs Scraper: scored to your CV",
    "description": "First 25 jobs free. AI job match for LinkedIn jobs: scrapes your search, drops reposts, agencies and duplicates free, scores each job 0-100 against your CV with a recruiter note. Pay per matched job, no start fee. Free demo on any plan; live search runs on your own Apify account.",
    "version": "0.3",
    "x-build-id": "Zizem3jU2HOkHck91"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/rich_minds~linkedin-job-match-ai/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-rich_minds-linkedin-job-match-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/rich_minds~linkedin-job-match-ai/runs": {
      "post": {
        "operationId": "runs-sync-rich_minds-linkedin-job-match-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/rich_minds~linkedin-job-match-ai/run-sync": {
      "post": {
        "operationId": "run-sync-rich_minds-linkedin-job-match-ai",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "searchKeywords": {
            "title": "Job search terms",
            "type": "array",
            "description": "One LinkedIn job search per term (job title, skill or both, e.g. <i>python developer</i>). Each search returns up to <b>Jobs per search</b> jobs. Type your own terms next to the sample and press Start: that starts the live search (capped by <b>Max source spend</b>) — LinkedIn's public job pages are read by this Actor on any Apify plan; the fallback scraper runs on a plan that runs Store Actors.",
            "items": {
              "type": "string"
            }
          },
          "preset": {
            "title": "Start from a ready niche search (one click)",
            "enum": [
              "custom",
              "remote-python-alerts",
              "h1b-sponsorship-alerts",
              "remote-europe-alerts",
              "new-grad-software"
            ],
            "type": "string",
            "description": "Pick a ready job search and press Start: it fills the search terms and filters you left empty and runs the live search (spend-capped by <b>Max source spend</b>) — your CV still decides the score. Your own values always win. <b>Custom</b> = fill the fields yourself.",
            "default": "custom"
          },
          "candidateProfile": {
            "title": "Your CV / profile",
            "type": "string",
            "description": "Paste your CV summary or LinkedIn About: skills, years of experience, target roles, visa needs. The rules pull your skills from it and the AI scores every job against it. It is only sent to the model — never written to the dataset."
          },
          "minScore": {
            "title": "Minimum match score (0–100)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "The only field that changes what you pay: jobs whose final match score is below this are dropped and <b>not charged</b>. Raise it to pay only for the strongest matches, lower it to see more. 80+ = strong, 65+ = good, 50+ = stretch.",
            "default": 60
          },
          "maxQualified": {
            "title": "Max matched jobs to output",
            "minimum": 1,
            "maximum": 10000,
            "type": "integer",
            "description": "Affects what you pay: hard cap on results and the ceiling of what <b>this Actor</b> charges — at most this many × $0.01 with AI on (× $0.002 with AI off), e.g. 100 × $0.01 = $1.00 here; the source's own spend has its cap right below. The best matches are output first.",
            "default": 100
          },
          "maxDiscoveryChargeUsd": {
            "title": "Max source spend (USD)",
            "minimum": 0.01,
            "maximum": 1000,
            "type": "number",
            "description": "Affects what you pay: hard cap on what the LinkedIn scraper may charge your account when it runs — $0.50 in the form and for an API or agent call that leaves it out, so a first real search can never cost more. The built-in reader of LinkedIn's public pages costs nothing here. Raise it for bigger searches.",
            "default": 0.5
          },
          "enableAi": {
            "title": "AI match & recruiter note",
            "type": "boolean",
            "description": "Score each job against your CV with grounded reasons, extract its real requirements (years, must-haves, sponsorship with the quoted sentence) and draft a note to the poster plus a cover-letter hook for strong and good matches. <b>On = $0.01 per matched job + AI tokens; off = $0.002</b> (rule-based score only). Needs <b>Your CV / profile</b>. Your first 5 jobs get live AI verdicts on any plan; all 25 free jobs with Apify model access or a paid own key. Non-English job posts: leave the AI on — the rules read English.",
            "default": true
          },
          "notifyEmail": {
            "title": "E-mail me the new jobs (optional)",
            "type": "string",
            "description": "After every run with at least one new matched job, the run's digest (how many are new, the top 10 with links and why they match, the market report) is e-mailed here through Apify's send-mail Actor. Schedule the run daily and the jobs come to your inbox — no Slack or Zapier needed. Never sent for the free demo."
          },
          "location": {
            "title": "Location",
            "type": "string",
            "description": "City, region or country as you would type it on LinkedIn (e.g. <i>United States</i>, <i>London, United Kingdom</i>, <i>Remote</i>). Empty = worldwide.",
            "default": "United States"
          },
          "datePosted": {
            "title": "Posted within",
            "enum": [
              "anyTime",
              "past24Hours",
              "pastWeek",
              "pastMonth"
            ],
            "type": "string",
            "description": "Only jobs posted in this window. <b>Past 24 hours</b> is the daily-alert setting.",
            "default": "pastWeek"
          },
          "maxJobsPerSearch": {
            "title": "Jobs per search",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Affects what you pay when the LinkedIn scraper runs: how many jobs each search term scrapes (the source's <code>limitPerSource</code>, $0.002 each on your account; free with the built-in reader). Cut automatically so all searches stay inside <b>Max source spend</b>.",
            "default": 40
          },
          "candidateProfileUrl": {
            "title": "…or a link to your CV",
            "type": "string",
            "description": "Instead of pasting: a public link to your CV as PDF, DOCX or text — e.g. your LinkedIn profile saved as PDF (profile → <b>More → Save to PDF</b>) uploaded to an Apify key-value store on your account or any file host. Its text is read at the start of the run and added to <b>Your CV / profile</b>. A LinkedIn profile page itself needs a login and cannot be read."
          },
          "needsVisaSponsorship": {
            "title": "I need visa sponsorship",
            "type": "boolean",
            "description": "Drop jobs whose description says the employer will not sponsor a visa (\"unable to sponsor\", \"no sponsorship\", \"U.S. citizens only\"); the sentence is kept as evidence. The AI also weighs it.",
            "default": false
          },
          "workplace": {
            "title": "Workplace",
            "enum": [
              "any",
              "remote",
              "hybrid",
              "onsite"
            ],
            "type": "string",
            "description": "Keep only remote, hybrid or on-site jobs (from LinkedIn's workplace type; unknown ones are kept).",
            "default": "any"
          },
          "mustHaveSkills": {
            "title": "Must-have skills",
            "type": "array",
            "description": "Skills the job should ask for (word-boundary match: <i>Java</i> never matches <i>JavaScript</i>). They weigh most in the match score and fill <code>matchedSkills</code>.",
            "items": {
              "type": "string"
            }
          },
          "niceToHaveSkills": {
            "title": "Nice-to-have skills",
            "type": "array",
            "description": "Skills that raise the score when the job mentions them.",
            "items": {
              "type": "string"
            }
          },
          "excludeKeywords": {
            "title": "Exclude jobs mentioning",
            "type": "array",
            "description": "Drop every job whose title or description mentions one of these (e.g. <i>unpaid</i>, <i>commission only</i>, <i>clearance</i>).",
            "items": {
              "type": "string"
            }
          },
          "targetSeniority": {
            "title": "Seniority",
            "type": "array",
            "description": "Keep only these LinkedIn seniority levels: <i>Internship</i>, <i>Entry level</i>, <i>Associate</i>, <i>Mid-Senior level</i>, <i>Director</i>, <i>Executive</i> (jobs without a level are kept). LinkedIn's new AI search no longer filters this reliably — this is the strict post-filter.",
            "items": {
              "type": "string"
            }
          },
          "employmentTypes": {
            "title": "Employment type",
            "type": "array",
            "description": "Keep only these employment types: <i>Full-time</i>, <i>Part-time</i>, <i>Contract</i>, <i>Temporary</i>, <i>Internship</i> (jobs without one are kept).",
            "items": {
              "type": "string"
            }
          },
          "minSalary": {
            "title": "Salary floor",
            "minimum": 0,
            "type": "integer",
            "description": "Drop jobs whose stated top salary is below this (in <b>Salary currency</b> per <b>Salary period</b>; hourly and yearly are converted at 2,080 hours). Jobs without a salary are kept unless you tick the box below. 0 = off.",
            "default": 0
          },
          "salaryCurrency": {
            "title": "Salary currency",
            "type": "string",
            "description": "ISO code of your salary floor, e.g. USD, EUR, GBP. Jobs paid in another currency are not compared.",
            "default": "USD"
          },
          "salaryPeriod": {
            "title": "Salary period",
            "enum": [
              "year",
              "month",
              "hour"
            ],
            "type": "string",
            "description": "The period of your salary floor.",
            "default": "year"
          },
          "dropJobsWithoutSalary": {
            "title": "Drop jobs without a stated salary",
            "type": "boolean",
            "description": "Only with a salary floor: also drop jobs that state no salary.",
            "default": false
          },
          "maxApplicants": {
            "title": "Max applicants",
            "minimum": 0,
            "type": "integer",
            "description": "Drop jobs that already have more applicants than this. 0 = off.",
            "default": 0
          },
          "maxJobAgeDays": {
            "title": "Max job age (days)",
            "minimum": 0,
            "type": "integer",
            "description": "Drop jobs posted more than this many days ago. 0 = off.",
            "default": 0
          },
          "dropGhostJobs": {
            "title": "Drop likely ghost jobs",
            "type": "boolean",
            "description": "Drop postings with a <b>high</b> ghost-job risk — at least three points from: reposted under a new job id, open for 30+ days (60+ counts double), 200+ applicants, no named poster. Every delivered job carries <code>ghostJobRisk</code> (low / medium / high) and its reasons either way.",
            "default": false
          },
          "excludeCompanies": {
            "title": "Exclude companies",
            "type": "array",
            "description": "Company names to never show (your current employer, companies you already applied to).",
            "items": {
              "type": "string"
            }
          },
          "excludeStaffingAgencies": {
            "title": "Drop staffing agencies",
            "type": "boolean",
            "description": "Drop jobs posted by staffing / recruiting agencies (company name, industry or \"our client\" wording).",
            "default": true
          },
          "minCompanyEmployees": {
            "title": "Min company size (employees)",
            "minimum": 0,
            "type": "integer",
            "description": "Drop companies smaller than this (when LinkedIn states the size). 0 = off.",
            "default": 0
          },
          "maxCompanyEmployees": {
            "title": "Max company size (employees)",
            "minimum": 0,
            "type": "integer",
            "description": "Drop companies larger than this (when LinkedIn states the size). 0 = off.",
            "default": 0
          },
          "targetFlags": {
            "title": "Only jobs with these flags (any of)",
            "type": "array",
            "description": "Only deliver jobs with at least one of these flags. An API call passes the flag names (<code>sponsorship_offered</code> …; the camelCase output keys such as <code>sponsorshipOffered</code> work too); an unknown name stops the run at once with the list of valid ones.",
            "items": {
              "type": "string",
              "enum": [
                "sponsorship_offered",
                "no_sponsorship",
                "clearance_required",
                "relocation_offered",
                "staffing_agency",
                "language_required"
              ],
              "enumTitles": [
                "Offers visa sponsorship",
                "Says no visa sponsorship",
                "Needs a security clearance",
                "Offers relocation",
                "Posted by a staffing agency",
                "Needs another language"
              ]
            }
          },
          "suppressionList": {
            "title": "Never show these jobs",
            "type": "array",
            "description": "LinkedIn job ids, job URLs or exact titles to never output. Skipped before any processing and never charged.",
            "items": {
              "type": "string"
            }
          },
          "generateRecruiterNote": {
            "title": "Write the recruiter note and cover-letter hook",
            "type": "boolean",
            "description": "For strong and good matches only; greets the poster by name when LinkedIn shows one. Off = score and requirements only.",
            "default": true
          },
          "noteTone": {
            "title": "Note tone",
            "enum": [
              "professional",
              "friendly",
              "concise"
            ],
            "type": "string",
            "description": "How the recruiter note sounds.",
            "default": "professional"
          },
          "llmProvider": {
            "title": "AI model access",
            "enum": [
              "apify",
              "byok"
            ],
            "type": "string",
            "description": "<b>Apify (no keys)</b> — the AI runs through Apify's built-in OpenRouter proxy on plans with model access; tokens are billed to your Apify account at OpenRouter's rates. <b>My own key</b> — your OpenAI / Anthropic / Gemini / Groq key instead (works on every plan). A free-tier key assesses only a few jobs per run — expect 1–3 AI matches (2 calls at a time, one after a rate limit, then the 3 borderline jobs retried one by one) — and the rest keep their rule score; a paid Groq / Gemini key (billing on) assesses every job.",
            "default": "apify"
          },
          "llmApiKey": {
            "title": "Your API key (own-key mode only)",
            "type": "string",
            "description": "Required when <b>AI model access</b> is <i>My own API key</i>. Stored encrypted by Apify, never logged."
          },
          "llmModel": {
            "title": "AI model",
            "type": "string",
            "description": "Leave empty for the default (<code>anthropic/claude-haiku-4.5</code>). Apify mode takes an OpenRouter slug such as <code>openai/gpt-4.1-mini</code>; own-key mode takes <code>provider:model</code>, e.g. <code>anthropic:claude-haiku-4-5-20251001</code>."
          },
          "aiCandidateMultiplier": {
            "title": "AI candidate pool (× max matched)",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "How many of the best rule-scored jobs get the AI pass, as a multiple of <b>Max matched jobs</b> (only jobs within 20 points of the minimum score). Higher = more thorough, slower, more tokens.",
            "default": 2
          },
          "sendAlertsTo": {
            "title": "Send alerts to (Slack, Discord, Teams, Google Chat, Telegram or webhook)",
            "type": "string",
            "description": "Paste one or more URLs, one per line — the service is detected from the address: a Slack or Discord channel webhook, a Teams Workflows or Google Chat space webhook, your Telegram bot's send link (<code>https://api.telegram.org/bot&lt;token&gt;/sendMessage?chat_id=@yourchannel</code>) or any other https URL (Zapier, Make, n8n, your CRM), which gets the jobs as JSON. After every run with at least one new job, the digest (how many are new, the top 10 with links) is posted there. Never sent for the free demo. Headers, batching and signing: <i>Delivery details (advanced)</i>."
          },
          "candidates": {
            "title": "Several candidates (coaches, recruiters)",
            "type": "array",
            "description": "Score one search for a whole roster: a JSON array, one object per candidate with <code>name</code> and <code>candidateProfile</code> (or <code>candidateProfileUrl</code>), optionally their own <code>searchKeywords</code>, <code>mustHaveSkills</code>, <code>niceToHaveSkills</code>, <code>targetSeniority</code>, <code>workplace</code>, <code>minSalary</code>, <code>needsVisaSponsorship</code>, <code>minScore</code>, <code>maxQualified</code> … Every term is searched once for all; each candidate gets their own score, rows (<code>candidateName</code>), memory of jobs already received and DIGEST section. One matched-job event per job per candidate. Leave empty for your own search."
          },
          "dedupeAcrossRuns": {
            "title": "Only new jobs on every run",
            "type": "boolean",
            "description": "Remembers every job you received — by LinkedIn id and by company + title + location, so reposts are caught — in a named key-value store on your account for <b>Deliver a job again after</b> days (60), and skips them next time. Each CV keeps its own memory; a job already paid for under an earlier CV is delivered as <code>already-charged</code>, never billed twice. Keep it on for a daily schedule.",
            "default": true
          },
          "dedupeTtlDays": {
            "title": "Deliver a job again after (days)",
            "minimum": 0,
            "maximum": 3650,
            "type": "integer",
            "description": "How long a delivered job stays remembered (the default: 60 days — a LinkedIn posting is long closed by then). 0 = never deliver or charge it again. A job that is reposted after this many days reaches you again, charged as new.",
            "default": 60
          },
          "historyDatasetName": {
            "title": "Keep a running log (dataset name)",
            "type": "string",
            "description": "Optional. Type a name (e.g. <code>linkedin-job-match-ai-seen-log</code>) and every run appends its new jobs with the date to that named dataset on your account — one list across months to connect to Google Sheets or your BI tool once. Empty = off. Costs nothing extra.",
            "default": ""
          },
          "searchUrls": {
            "title": "LinkedIn search URLs",
            "type": "array",
            "description": "Public LinkedIn job-search URLs (open linkedin.com/jobs/search in a private window, set the filters, copy the URL). Searched in one extra run next to the search terms.",
            "items": {
              "type": "string"
            }
          },
          "geoId": {
            "title": "LinkedIn geoId",
            "type": "string",
            "description": "Precise location id from a LinkedIn search URL (<code>geoId=</code>). Overrides the free-text location when LinkedIn resolves it."
          },
          "distance": {
            "title": "Radius (miles)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "Search radius around the location. 0 = LinkedIn's default.",
            "default": 0
          },
          "companyIds": {
            "title": "Only these companies (LinkedIn ids)",
            "type": "array",
            "description": "LinkedIn numeric company ids (from a company URL or <code>f_C=</code> in a search URL), e.g. 1441 for Google.",
            "items": {
              "type": "string"
            }
          },
          "under10Applicants": {
            "title": "Only jobs with fewer than 10 applicants",
            "type": "boolean",
            "description": "Ask LinkedIn for early-bird jobs only.",
            "default": false
          },
          "splitByLocation": {
            "title": "Split the search by city",
            "type": "boolean",
            "description": "Gets past LinkedIn's 1,000-jobs-per-search cap by searching each city of <b>Split country</b> separately (more jobs, more source cost).",
            "default": false
          },
          "splitCountry": {
            "title": "Split country (ISO code)",
            "type": "string",
            "description": "Two-letter country code whose cities are searched, e.g. US, GB, DE. Only with <b>Split the search by city</b>."
          },
          "sourceMode": {
            "title": "Where do the jobs come from?",
            "enum": [
              "actor",
              "dataset",
              "list"
            ],
            "type": "string",
            "description": "<b>Search LinkedIn</b> — the default: your search terms are read from LinkedIn's public job pages (or by <code>curious_coder/linkedin-jobs-scraper</code> on your account, about $0.002 per job, on a plan that runs Store Actors). <b>Existing dataset</b> — match the jobs of an earlier run (no new scraping cost). <b>Paste jobs</b> — the free demo: the sample jobs, or your own list. The mode you choose always runs; only the untouched sample plus search terms you typed switches to the live search. An API call that names nothing to search (<code>{}</code>: no search terms, URLs or CV) runs the free demo on the sample jobs instead — nothing is charged and the source Actor is never started. <b>Bring your own data:</b> rows or a dataset ID, and you pay only for the analysis.",
            "default": "actor"
          },
          "autoSwitchToLive": {
            "title": "Typed search next to the sample starts the live search",
            "type": "boolean",
            "description": "On (default): search terms typed next to the untouched sample records start the live search (spend-capped; <code>OUTPUT.switchedToLive</code> names them). Off: the mode you send is the mode that runs — the sample stays the free demo and the status names the terms it did not search. API pipelines that must never start a paid read on their own: send <code>false</code>.",
            "default": true
          },
          "itemsList": {
            "title": "Jobs to process",
            "type": "array",
            "description": "Only for <b>Paste jobs</b> mode. JSON array of jobs in the shape of <code>curious_coder/linkedin-jobs-scraper</code> (at least <code>title</code>; <code>id</code>, <code>link</code>, <code>companyName</code>, <code>descriptionText</code> … as in its output)."
          },
          "datasetId": {
            "title": "Dataset",
            "type": "string",
            "description": "Only for <b>Existing dataset</b> mode. Pick a dataset of an earlier <code>curious_coder/linkedin-jobs-scraper</code> run so the Actor is granted read access to it."
          },
          "ownSource": {
            "title": "Read LinkedIn's public job pages directly (no Store Actor)",
            "type": "boolean",
            "description": "On (default): job searches by keyword are read by this Actor itself from LinkedIn's public job pages — no second scraper bill on your account, works on every Apify plan. It reads title, company, location, posted date, applicant count, salary when shown, seniority, employment type, job function, industries and the description; it does <b>not</b> read company size or website, or the recruiter. Search URLs, the advanced source input, company-size filters and split-by-location use the Store Actor. If LinkedIn blocks the direct read, the Store Actor runs as before. Off: always the Store Actor.",
            "default": true
          },
          "discoveryActorId": {
            "title": "Source Actor",
            "type": "string",
            "description": "Actor used in <b>Search LinkedIn</b> mode. Any Actor whose output has the fields listed in the README works.",
            "default": "curious_coder/linkedin-jobs-scraper"
          },
          "discoveryInput": {
            "title": "Extra source input (JSON)",
            "type": "object",
            "description": "Merged over the input built from the fields above and passed to the source Actor as-is — for options this form does not show. See the source Actor's input schema."
          },
          "maxToProcess": {
            "title": "Max jobs to process",
            "minimum": 1,
            "maximum": 20000,
            "type": "integer",
            "description": "Upper bound on how many jobs pass the free filters and get scored in one run (controls run time). Default = 3 × max matched."
          },
          "webhookUrl": {
            "title": "Webhook URL (optional)",
            "type": "string",
            "description": "Matched jobs are POSTed here as JSON (Zapier, Make, n8n, Notion, Airtable) — or paste it into <b>Send alerts to</b>. A POST that fails is retried once; its jobs are listed in <code>OUTPUT.webhook.failedKeys</code> and not re-sent next run. For Google Sheets you can also use Apify's Integrations tab."
          },
          "webhookHeaders": {
            "title": "Webhook headers",
            "type": "object",
            "description": "Extra HTTP headers for the webhook, e.g. <code>{\"Authorization\": \"Bearer …\"}</code>."
          },
          "webhookBatchSize": {
            "title": "Webhook batch size",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "1 = one POST per job the moment it is ready. Higher = one POST per N jobs.",
            "default": 1
          },
          "webhookSecret": {
            "title": "Webhook signing secret (optional)",
            "type": "string",
            "description": "Any random string. Every webhook POST then carries <code>X-Signature: sha256=&lt;HMAC-SHA256 of the raw body with this secret&gt;</code>, so your endpoint can verify it came from this Actor. Stored encrypted by Apify, never logged."
          },
          "dedupeStoreName": {
            "title": "Dedupe store name",
            "type": "string",
            "description": "Key-value store used for cross-run memory. Left at the default, the memory is kept per CV: another <code>candidateProfile</code> (another candidate) starts its own list, so one candidate never hides jobs from another. Type your own name to keep one memory across CV edits, or one per campaign.",
            "default": "linkedin-job-match-ai-seen"
          },
          "discoveryTimeoutSecs": {
            "title": "Source run timeout, seconds (API only)",
            "minimum": 60,
            "maximum": 86400,
            "type": "integer",
            "description": "API only — hidden on the form. How long the source Store Actor may run before its partial results are used. Raise it when the status says the source stopped early.",
            "default": 3600
          },
          "slackWebhookUrl": {
            "title": "Slack webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Slack incoming-webhook URL for the digest."
          },
          "discordWebhookUrl": {
            "title": "Discord webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Discord channel webhook URL for the digest."
          },
          "teamsWebhookUrl": {
            "title": "Microsoft Teams webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Teams Workflows webhook URL for the digest."
          },
          "googleChatWebhookUrl": {
            "title": "Google Chat webhook URL (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Google Chat space webhook URL for the digest."
          },
          "telegramUrl": {
            "title": "Telegram sendMessage link (API only)",
            "type": "string",
            "description": "API only — hidden on the form, where <code>sendAlertsTo</code> takes it. The bot's sendMessage link with its chat_id."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}