Unified.to
All articles

How to Push Candidates and Applications into ATS Systems with Unified's ATS API


February 2, 2026

Ingesting ATS data is one half of an integration.

The other half is write-side control: creating candidates, submitting applications, attaching documents, and updating pipeline state inside your customer's ATS.

Write operations are more complex than reads. Field requirements vary by provider. Writable fields differ across integrations. Some objects are read-only in certain ATSs. There is no global stage-transition model. And Unified does not enforce idempotency or duplicate prevention for you.

This guide explains how to push candidates and applications into ATS systems using Unified's ATS API — accurately, safely, and with provider variability in mind.

The write mental model

Write operations follow this typical flow:

  1. Create a candidate
  2. Create an application linking candidate to job
  3. (Optional) Upload documents (resume, cover letter, etc.)
  4. (Optional) Update application status
  5. Handle errors and retry appropriately

Everything else — deduplication, idempotency, cross-connection identity — belongs in your application layer.

Step 1: Create a Candidate

Endpoint

POST /ats/{connection_id}/candidate

SDK:

const result = await sdk.ats.createAtsCandidate({
  connectionId,
  atsCandidate: {
    first_name: 'Jane',
    last_name: 'Doe',
    emails: [{ email: 'jane@example.com', type: 'WORK' }],
  },
  fields: '',
  raw: '',
});

Required fields

The generic ATS create candidate endpoint does not document any required fields beyond the connection_id path parameter.

However, required fields vary by integration.

Examples documented:

  • Vincere requires emails and name (starred writable fields).
  • Other providers (e.g., Greenhouse, Lever) do not mark required candidate fields in documentation.

Conclusion:

You must check the integration's 'Writable Fields' section. Starred fields are required for that provider.

If no fields are starred, no required fields are documented.

Duplicate prevention & idempotency

Unified documentation:

  • Does not document idempotency key support.
  • Does not document upsert behavior.
  • Does not document duplicate candidate prevention.
  • Does not enforce uniqueness of external_identifier.

Therefore:

Unified documentation does not describe idempotency or duplicate prevention for ATS write endpoints.

If your system may send the same candidate twice, implement idempotency and dedupe logic in your own application.

Step 2: Create an Application

Applications link candidates to jobs.

Endpoint

POST /ats/{connection_id}/application

SDK:

const result = await sdk.ats.createAtsApplication({
  connectionId,
  atsApplication: {
    candidate_id: 'candidate123',
    job_id: 'job456',
    status: 'NEW',
  },
  fields: '',
  raw: '',
});

Required fields

The generic documentation does not mark required fields.

However, integration pages show:

  • Vincere: candidate_id and job_id are required (starred).
  • Lever: candidate_id and job_id are required.
  • Greenhouse: no required fields marked, but logically candidate_id and job_id must exist to create a valid application.

Safe rule:

Assume candidate_id and job_id are required unless the integration page explicitly states otherwise.

Writable fields on create

Writable fields vary by integration.

Common patterns:

FieldWritable?Notes
candidate_idYesUsually required
job_idYesUsually required
statusOften yesWritable in many integrations
original_statusOnly someWritable in some providers (e.g., Greenhouse)
applied_atSomeWritable in some integrations (e.g., Vincere), not others
offers[]NoNot listed in writable fields in reviewed integrations

If a field is not listed under 'Writable Fields' on the integration page, it should be treated as read-only.

Step 3: Upload Documents (Resume, etc.)

Endpoint

POST /ats/{connection_id}/document

Required behavior

  • document_data must be base64 encoded when used.
  • Some integrations support only document_url.
  • Some support both.
  • File size and file type limits are not documented by Unified.
  • Attachment target varies by provider.

Provider-specific attachment behavior

ProviderAttach ToBase64 SupportNotes
Greenhouseapplication onlyNoRequires document_url
Levercandidate onlyYesRequires candidate_id, filename
Workableapplication onlyYesRequires application_id, filename, type
SmartRecruiterscandidate or jobNoURL-based upload
CATScandidate onlyYesSupports base64

There are no documented file size or file type restrictions in Unified's docs.

Step 4: Update an Application

Endpoint

PUT /ats/{connection_id}/application/{id}

SDK:

const result = await sdk.ats.updateAtsApplication({
  connectionId,
  id: 'application123',
  atsApplication: {
    status: 'REVIEWING',
  },
  fields: '',
  raw: '',
});

Writable fields on update

Writable fields depend on integration.

Examples:

  • Vincere: candidate_id, job_id, applied_at, status
  • Greenhouse: candidate_id, job_id, answers, status, original_status
  • Lever: candidate_id, job_id, answers, status

offers[] is not writable in reviewed integrations.

Status behavior

  • status is writable in the reviewed integrations.
  • Some providers classify status as a 'slow field' (updates propagate more slowly).
  • original_status is writable only if listed in writable fields.
  • No documented stage transition rules exist at the Unified level.
  • Provider-side constraints may apply but are not documented in Unified.

Error Handling & Retry

Unified's OpenAPI spec for ATS write endpoints documents only successful 200 responses.

No structured ATS-specific error schema is documented.

Observations:

  • Errors follow standard HTTP status codes.
  • 400 for malformed or invalid requests.
  • 429 for rate limiting (provider-driven).
  • 5XX for provider outages.

The SDK exposes generic error handling objects (e.g., UnifiedToError).

Retry behavior

  • Unified recommends exponential backoff for rate limits.
  • Python SDK includes retry/backoff utilities.
  • No ATS-specific retry guarantees are documented.

Implement:

  • Exponential backoff
  • Retry limits
  • Logging
  • Provider-aware throttling

Safe Write-Side Architecture

A safe pattern for pushing candidates and applications:

  1. Check whether candidate exists in your system.
  2. Create candidate in ATS.
  3. Create application linking to job.
  4. Upload documents if supported.
  5. Update status as needed.
  6. Implement idempotency in your app layer.
  7. Handle retries with exponential backoff.
  8. Validate integration-specific writable fields before sending payload.

Write-side ATS integration requires more discipline than read-side ingestion

Field support varies. Required fields differ. Status updates are provider-specific. Offers cannot be written at creation time. Duplicate handling is not enforced. Error contracts are not strongly typed in documentation.

Unified's ATS API exposes the underlying provider capabilities through a normalized interface. It does not abstract away provider-level write constraints.

Treat each integration's 'Writable Fields' section as authoritative, and build your write workflows accordingly.

From there, pushing candidates and applications becomes predictable — even across multiple ATS systems.

Start your 30-day free trial

Book a demo

All articles