The short answer
Give each candidate a stable candidateId, attach documents to that candidate, and select the exact resume and cover letter on each application. Keep billing centralized while preserving candidate-specific state.
Model a candidate independently from your developer account
Create a candidate with an externalId that maps to your own user record and candidateContext containing that person's information. Address, LinkedIn, education, experience, and preferences belong to that candidate's context rather than a shared account-wide profile.
Retain the returned candidate ID in your database. Creating the same person again for every application makes document selection and status attribution harder. Use your own authenticated backend to decide which of your users may act on each candidate; do not expose a shared developer API key in a browser.
Select documents per application
Upload a resume or cover letter to the candidate's document endpoint. Keep the returned document ID with the job and document version it was created for. Pass resumeId and, when appropriate, coverLetterId alongside candidateId when creating the application.
A candidate's default resume is convenient for repeated applications, but changing a shared default is a poor way to coordinate concurrent tailored applications. Explicit document selection makes each intended submission reviewable. Document ownership and purpose must match the candidate and request.
import { readFile } from "node:fs/promises";
import { ApplyGuy } from "@applyguy/sdk";
const apiKey = process.env.APPLYGUY_API_KEY;
const candidateId = process.env.CANDIDATE_ID;
const jobId = process.env.JOB_ID;
if (!apiKey || !candidateId || !jobId) throw new Error("Missing environment variables");
const client = new ApplyGuy({ apiKey });
const file = new Blob([await readFile("./tailored-resume.pdf")], { type: "application/pdf" });
const { data: resume } = await client.candidates.uploadDocument(
candidateId, "resume", file, "tailored-resume.pdf",
);
const { data: check } = await client.applications.preflight({
candidateId, jobId, resumeId: resume.id, mode: "managed",
});
console.log({ resumeId: resume.id, ready: check.ready, cost: check.cost });
// This example uploads a document and checks readiness; it does not submit.Track one application resource through its lifecycle
After candidate approval, create the application with a stable idempotency key and persist its returned ID. Store the candidate and job mapping in your own system as well. A queue acknowledgement is not a successful employer submission.
Use application status retrieval or webhook events to update your UI. Verify webhook signatures according to the current reference, handle duplicate deliveries, and avoid letting a delayed event overwrite a newer known state. Present failures and verification requirements accurately instead of retrying every outcome automatically.
Keep your own AI and candidate verification separate
Agent mode can pause an application with a discovered answer request. Use its prompt, schema, request ID, and fingerprint with your own model, then return validated answers through the documented endpoint. Do not invent form questions before discovery or treat one answer round as the entire application.
Email integration is candidate-specific where required. Connect only the mailbox the candidate has authorized and follow the supported setup. Secret fields are not part of ordinary candidate profile reads; your own service must also prevent logging credentials, exposing them to other users, or including them in analytics.
Keep billing central and usage attributable
API credits belong to the developer account. Map applications and document versions back to your users so your own dashboard can explain who applied, which material was used, and the result. Read current costs from the API and documentation rather than baking an old price into a long-lived integration.
Start with a small authorized end-to-end test across the ATSs your users actually need. Validate profile completeness, supported US targets, documents, inbox requirements, answer rounds, and terminal results before opening submission to a larger audience.
Sources and further reading
Provider and employer requirements can change. Follow the instructions for the particular application or integration.