Custom ATS Integration
Build a custom API so Wintro can sync jobs and referrals with your Applicant Tracking System.
If your ATS is not in our list of supported integrations, you can build a simple REST API that Wintro will call to keep your jobs and referrals in sync. This guide describes exactly what your API needs to look like.
How it works
Wintro calls your API — you never need to call ours. Once connected, the sync runs automatically:
- Wintro pulls your open jobs every hour so employees can refer candidates.
- When an employee submits a referral, Wintro pushes the candidate into your ATS pipeline.
- Wintro tracks each referral’s progress by polling your API hourly, and notifies the referring employee when their candidate advances.
Your API just needs to respond to a handful of endpoints — Wintro handles scheduling, retries, and error recovery.
Getting started
To set up the integration, share the following with your Wintro contact:
- Base URL — Where your API is hosted (e.g.
https://ats.yourcompany.com). - Authentication credentials — An API key or OAuth 2.0 client credentials (see Authentication).
- Rate limits — Any request limits Wintro should respect (see Rate limiting).
- Stage mapping — Which of your pipeline stages correspond to Wintro’s states (see Pipeline mapping).
Wintro will configure the connection on our side and run an initial sync to pull in your open jobs.
What Wintro syncs
Jobs
Wintro pulls all open positions from your ATS every hour. Each job appears on the Wintro platform where employees can browse and make referrals. When a job is closed or archived in your ATS, Wintro automatically removes it.
Referrals
When an employee submits a referral through Wintro:
- Wintro creates an application in your ATS for the referred candidate.
- Attaches a note with referral context (who referred them, why they’re a fit).
- Tags the candidate as a “Wintro Referral” for easy filtering.
- Places the candidate in your configured entry stage (e.g. “New Referral”).
Referral progress
Once a referral is created, Wintro checks your API hourly for status changes:
- Detects stage transitions (e.g. Applied → Phone Screen → Interview → Offer).
- Updates the referral status in Wintro accordingly.
- Notifies the referring employee when their candidate advances, gets hired, or is rejected.
Pipeline mapping
During setup, you’ll map your ATS pipeline stages to Wintro’s internal states. This tells Wintro how to interpret your candidate’s journey:
| Wintro state | What it means | Example stages in your ATS |
|---|---|---|
| Entry | Where new referrals land | ”New Referral”, “Applied” |
| Interview | Actively being evaluated | ”Phone Screen”, “Onsite”, “Reference Check” |
| Exclusion | Removed from consideration (not final) | “Not Qualified”, “No Show” |
| Rejection | Final “no" | "Rejected”, “Declined” |
| Hired | Final “yes" | "Offer Accepted”, “Hired” |
:::tip
If your ATS already provides an outcome field on applications (e.g. PENDING, HIRED, REJECTED), Wintro can use that directly — you only need to map entry and exclusion stages.
:::
API specification
Below are the endpoints your API needs to expose. The URL paths are suggestions — as long as the request/response formats match, the exact paths can differ.
Overview
| Endpoint | Method | Purpose | Called |
|---|---|---|---|
| Get jobs | GET | All open positions with stages | Hourly |
| Get candidates for a job | GET | Candidates in a specific job pipeline | Hourly |
| Get candidates by ID | GET | Bulk status lookup for tracked referrals | Hourly |
| Create application | POST | Submit a new referral | On referral |
| Add note | POST | Attach referral context | On referral |
| Add tag | POST | Tag as “Wintro Referral” | On referral |
| Get stages | GET | List all pipeline stages | On setup |
1. Get jobs
GET /api/jobs
Returns all open positions. Wintro calls this hourly to sync your job listings.
Response:
[
{
"id": "job-123",
"title": "Senior Software Engineer",
"status": "open",
"location": "Brussels, Belgium",
"department": "Engineering",
"description": "<p>We're looking for...</p>",
"updated_at": "2025-03-01T12:00:00Z",
"stages": [
{ "id": "stage-1", "name": "Applied", "index": 1 },
{ "id": "stage-2", "name": "Phone Screen", "index": 2 },
{ "id": "stage-3", "name": "Interview", "index": 3 },
{ "id": "stage-4", "name": "Offer", "index": 4 }
]
}
]
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique job identifier |
title | string | Yes | Job title |
status | string | Yes | open, closed, or archived |
location | string | No | Job location |
department | string | No | Department name |
description | string | No | Job description (HTML or plain text) |
updated_at | ISO 8601 | Yes | Last modification timestamp |
stages | array | Yes | Ordered pipeline stages (see below) |
Each stage object:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique stage identifier |
name | string | Yes | Human-readable stage name |
index | number | Yes | Position in the pipeline (1 = first) |
:::note
Wintro uses updated_at for incremental syncing — only jobs that changed since the last sync are updated. Make sure this timestamp reflects any changes to the job posting, status, or stages.
:::
2. Get candidates for a job
GET /api/jobs/{jobId}/candidates
Returns all candidates in a specific job’s pipeline. Wintro calls this hourly per job to identify existing candidates and avoid suggesting duplicates.
Response:
[
{
"id": "candidate-456",
"first_name": "John",
"last_name": "Doe",
"application_id": "app-789",
"current_stage_id": "stage-2",
"outcome": null,
"changed_at": "2025-03-01T14:30:00Z"
}
]
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique candidate identifier |
first_name | string | Yes | Candidate first name |
last_name | string | Yes | Candidate last name |
application_id | string | Yes | Application identifier |
current_stage_id | string | Yes | Current pipeline stage ID |
outcome | string | null | Yes | PENDING, HIRED, or DECLINED |
changed_at | ISO 8601 | Yes | Last status change timestamp |
3. Get candidates by ID
GET /api/candidates?ids=candidate-456,candidate-789
Bulk lookup of specific candidates by their IDs. Wintro calls this hourly to track the progress of referred candidates through your pipeline.
Response: Same format as Get candidates for a job.
:::note This endpoint must support comma-separated IDs for bulk fetching. :::
4. Create application
POST /api/jobs/{jobId}/applications
Called when an employee submits a referral through Wintro. Creates a new candidate and application in your ATS.
Request body:
{
"first_name": "Jane",
"last_name": "Smith",
"email": "jane.smith@example.com",
"phone": "+32 470 12 34 56",
"resume_url": "https://storage.wintro.ai/cv/abc123.pdf",
"stage_id": "stage-1"
}
| Field | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | Candidate first name |
last_name | string | Yes | Candidate last name |
email | string | Yes | Candidate email |
phone | string | No | Candidate phone number |
resume_url | string | No | URL to download the resume/CV |
stage_id | string | No | Initial pipeline stage to place the candidate in |
Response:
{
"candidate_id": "candidate-456",
"application_id": "app-789"
}
Wintro needs both IDs to track the referral through your pipeline going forward.
5. Add note (optional)
POST /api/applications/{applicationId}/notes
Adds referral context to the candidate’s application. If your Create application endpoint supports a note field, this can be skipped.
Request body:
{
"content": "Wintro referral - Senior Software Engineer\n\nCandidate: John Doe\nReferrer: Jane Smith\nKnow how: Worked together at previous company for 3 years\nWhy fit: Has exact skills needed for this role, culture fit"
}
6. Add tag (optional)
POST /api/candidates/{candidateId}/tags
Tags the candidate as a Wintro referral for easy filtering in your ATS. If your Create application endpoint supports a tags field, this can be skipped.
Request body:
{
"tag": "Wintro Referral"
}
7. Get stages
GET /api/stages
Returns all available pipeline stages. Wintro calls this during initial setup so your team can map stages to Wintro states.
Response:
[
{ "id": "stage-1", "name": "Applied", "index": 1 },
{ "id": "stage-2", "name": "Phone Screen", "index": 2 },
{ "id": "stage-3", "name": "Interview", "index": 3 },
{ "id": "stage-4", "name": "Offer", "index": 4 },
{ "id": "stage-5", "name": "Hired", "index": 5 }
]
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique stage identifier |
name | string | Yes | Human-readable stage name |
index | number | Yes | Position in the pipeline (1 = first) |
Authentication
All endpoints must be secured with one of:
- API Key — Send via
Authorization: Bearer {api_key}header (simplest option). - OAuth 2.0 — Client credentials flow (recommended for production environments).
Share your chosen method and credentials with your Wintro contact during setup. Wintro stores all credentials encrypted.
Rate limiting
If your API has rate limits, let us know:
- Maximum requests per minute or hour.
- Any per-endpoint limits.
- Recommended batch sizes for bulk operations.
Wintro has built-in rate limiting and will adapt to your constraints.
Error handling
Return standard HTTP status codes:
| Status | Meaning |
|---|---|
200 | Success |
400 | Invalid input |
401 | Invalid or expired credentials |
404 | Resource not found (e.g. job was closed) |
429 | Rate limit exceeded |
500 | Server error |
For errors, return a JSON body so we can diagnose issues quickly:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests, retry after 60 seconds"
}
}
Wintro automatically retries on transient errors (429, 5xx) with exponential backoff.
Questions?
Reach out to your Wintro contact to discuss your integration. We’re happy to help review your API design before you start building.