JobScore to Pinpoint Migration: APIs, Mapping & Pitfalls
Technical guide to migrating from JobScore to Pinpoint ATS. Covers API constraints, entity mapping, rate limits, document handling, and compliance.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
JobScore to Pinpoint Migration: APIs, Mapping & Pitfalls
Migrating from JobScore to Pinpoint is a data-model translation problem. Both systems store candidates linked to applications linked to jobs, but the similarities end at the schema level. JobScore returns flat JSON via a traditional REST API. Pinpoint follows the JSON:API specification — a standard that wraps all responses in {"data": {"type": "...", "id": "...", "attributes": {...}, "relationships": {...}}} envelopes with compound documents (multiple resource types in one response) and relationship sideloading (related resources included in an included array rather than nested inline). Every field mapping decision — from workflow stages to source attribution to interview feedback — requires deliberate translation logic.
The first constraint most teams hit: JobScore's Hire API v2 is restricted to Enterprise customers and select partners. If your JobScore plan doesn't include API access, you're limited to filtered CSV exports that flatten relational data, drop interview feedback ratings, and strip document attachments entirely. For any dataset where you need to preserve candidate-application relationships, stage history, notes, or compliance data like EEO responses, API-based extraction is the only viable path. (developer.jobscore.com)
This guide covers the real API constraints on both sides, entity-by-entity mapping, viable migration approaches with trade-offs, rate-limit arithmetic, and the edge cases that silently corrupt data during ATS-to-ATS moves.
Disclosure: This guide is published by ClonePartner, a migration services provider. Technical claims are sourced from JobScore and Pinpoint documentation and are independently verifiable.
For related ATS migration patterns, see our ATS data migration checklist, ATS migration gotchas, and Pinpoint to Greenhouse migration guide.
Why This Migration Is Harder Than It Looks
A high-fidelity migration means preserving candidate identity, application relationships, documents, feedback, stage state, source attribution, and audit evidence — not just names and emails.
In JobScore, a candidate is the person record. That candidate can have zero or more applications, and workflow stages are customizable per job. JobScore maintains a single candidate-level source representing how you first came in contact with the person, even if they later apply to multiple jobs. Notes, interview feedback, tasks, and documents are separate activity objects associated with the candidate or application. (developer.jobscore.com)
In Pinpoint, the candidate profile is the master record that collates the person's applications, resumes, comments, interviews, messages, scorecards, documents, offers, and tags. The individual application is job-specific, and Pinpoint distinguishes candidate-level tags from application-level tags. Pinpoint collates applications into candidate profiles by matching on primary email address. When an incoming API candidate payload contains an email that already exists in Pinpoint, the system links the new application to the existing candidate profile rather than creating a duplicate — but this behavior is not an upsert: if you POST a candidate create with a duplicate email, verify whether your Pinpoint instance returns a conflict or silently links. Confirm current behavior in your specific Pinpoint account before relying on it for deduplication. Pinpoint also supports manual merging of candidate profiles in the UI. (help.pinpoint.support)
The structural mismatch that matters most: in Pinpoint, notes, scorecards, and stage movements belong to the Application, not the Candidate. If a candidate applied to a Sales role in 2023 and a Marketing role in 2025, Pinpoint isolates those two timelines completely. Migrating data to the wrong Pinpoint entity creates a confusing, overlapping history for hiring managers.
Source attribution is another mismatch. JobScore treats source as first-touch candidate provenance — one source per person, regardless of how many jobs they apply to. Pinpoint lets you set source attribution on each application via channel and channel_source. (support.jobscore.com)
JobScore API: Extraction Constraints
JobScore's Hire API v2 is the extraction layer. Here's what you're working with:
| Constraint | Detail |
|---|---|
| Endpoint | https://api.jobscore.com/v2 |
| Authentication | Personal Access Token via Authorization: Token token={your_token} header |
| Availability | Enterprise customers and select partners only |
| GET rate limit | 3,600 requests/hour (fixed window) |
| POST/PATCH/DELETE rate limit | 400 requests/hour, burst cap of 20 requests/minute |
| Pagination | Page-based, max 100 records per page |
| Date filtering | created_after, created_before, updated_after, updated_before on most endpoints |
| Timestamps | UTC / ISO 8601 only; non-UTC inputs are rejected |
JobScore's API access is gated behind the Enterprise plan. If you're on JobScore START or SCALE, you do not have API access. Confirm your plan includes the Hire API before planning an API-based migration.
What You Can Extract via the API
The Hire API v2 exposes these entities relevant to migration:
- Candidates — full profile: name, address, phone numbers, email addresses, profile links, employment history, education, tags, source attribution
- Applications — status (
new,active,declined,withdrawn,hired), current workflow stage, disposition code, linked documents - Jobs — title, internal title, requisition number, description (HTML + text), status, job types, remote type, compensation (public + private ranges, stored in cents), location, department, custom field answers, workflow stages
- Documents — resumes, cover letters, offer letters, note attachments; binary download via
GET /v2/documents/{document_id}/download - Notes — HTML content, privacy flag, author, linked job/application; includes interview feedback type with named ratings and numeric scores (1.0–4.0)
- EEO data — gender, race, veteran status, disability status; served via a dedicated
/v2/eeoendpoint - Offers — status, extended/accepted dates, start date, custom offer field values
- Tasks — type, start/end times, duration, attendees, candidate association
- Users, Departments, Locations, Tags, Disposition Codes, Workflow Stages — all supporting reference data
The 12 JobScore Source Types
JobScore defines exactly 12 source types. Each has a source_type string and an associated source_detail object with type-specific sub-fields:
JobScore source_type |
source_detail sub-fields |
Nearest Pinpoint channel |
|---|---|---|
website |
URL | careers_site |
job_board |
board name (e.g., "Indeed", "LinkedIn") | job_board |
referral |
referrer name, referrer email | referral |
social |
platform name | social_media |
email |
campaign name or sender | email |
event |
event name, event date | event |
user |
user ID, user name | direct |
internal |
internal program name | internal |
database |
database/resume bank name | resume_database |
search_firm |
agency name, recruiter name | recruiter |
network |
network description | referral |
other |
free-text description | undetermined |
Preserve the detail — "Job Board: Indeed" and "Job Board: LinkedIn" should remain distinguishable in the target system. Store the original source_detail value in channel_source (255-character limit) or a read-only custom field. Validate referral edge cases and internally-added records against how your reporting team uses source data today.
What JobScore's CSV Export Misses
JobScore's UI-based export lets you filter candidates by job, status, source, and score, then export to Excel. The documented export includes first name, last name, email, job title, date received, current status, source type, source detail, and referrer. (support.jobscore.com)
It is not a migration tool. The CSV export:
- Flattens the candidate-application relationship (a candidate with 3 applications becomes 3 disconnected rows)
- Drops interview feedback ratings entirely
- Omits note content and attachments
- Excludes EEO data
- Strips document binaries (you get filenames, not files)
- Loses stage-movement history and disposition codes
Pinpoint API: Ingestion Constraints
Pinpoint's API is the ingestion layer. Key differences from JobScore:
| Constraint | Detail |
|---|---|
| Endpoint | https://{subdomain}.pinpointhq.com/api/v1 |
| Authentication | API Key via X-API-KEY header |
| Spec | JSON:API (compound documents, relationship sideloading) |
| Rate limit | 120 requests/IP per ~1 minute, 240 requests/IP per ~8 minutes |
| Write support | Applications (create, update), Candidates (update), Jobs (update), Custom Attributes (read/write/delete), Custom Fields (read/write/delete) |
| Collection pagination | page [size] up to 1,000 |
Critical job creation gap: Pinpoint's documented write support for Jobs is update only — there is no POST /jobs endpoint in the public API. This means you cannot programmatically create new job records during migration. Jobs must be created manually in the Pinpoint UI (or via a Pinpoint implementation channel), and you must capture the resulting Pinpoint Job IDs before running any application imports. Verify current endpoint availability with Pinpoint support before finalizing your migration architecture, as this capability may change.
Pinpoint's rate limits are IP-based, not token-based. If you're running the migration from a single server, all requests count against the same window. At sustained throughput, you can push roughly 2 requests per second before hitting the limit. Always handle 429 Too Many Requests with adaptive backoff.
The JSON:API Difference
JobScore returns flat JSON objects. Pinpoint follows the JSON:API spec, wrapping data in {"data": {"type": "...", "id": "...", "attributes": {...}, "relationships": {...}}} structures. Compound documents let a single response include both an application and its related candidate in one payload; relationship sideloading means related resources appear in a top-level included array rather than nested inline. Your migration scripts need to handle this structural difference in both extraction (flat JSON from JobScore) and loading (JSON:API format for Pinpoint).
Pinpoint's channel Enum Values
Pinpoint's application channel field accepts a defined set of values. Submitting a value outside this enum will cause a 422 Unprocessable Entity error. The accepted values are:
careers_site, job_board, referral, social_media, email, event, direct, internal, resume_database, recruiter, undetermined
When no source is provided, Pinpoint defaults to undetermined. The channel_source field accepts free text up to 255 characters and is used to store the specific detail (e.g., board name, referrer name, agency).
Pinpoint API Error Responses
Understanding Pinpoint's error format is required before writing retry logic:
429 Too Many Requests— rate limit exceeded. Response includes aRetry-Afterheader indicating seconds to wait. Use this value in your backoff logic rather than a fixed interval.422 Unprocessable Entity— validation failure. Response body follows JSON:API error format:{"errors": [{"detail": "...", "source": {"pointer": "/data/attributes/channel"}}]}. Thesource.pointerfield identifies the specific failing attribute. Common causes: invalidchannelenum value, file type mismatch, missing required relationship, type mismatch on custom attribute.409 Conflict— resource conflict, e.g., attempting to create a duplicate unique resource.404 Not Found— referenced relationship ID does not exist (e.g., a Job ID that wasn't created yet). This is the most common sequencing error in migrations.
Log the full response body for every non-2xx response. The detail string in a 422 is the fastest path to diagnosing a field mapping problem.
File Constraints
Pinpoint's application endpoints accept CVs and cover letters in pdf, doc, docx, txt, and rtf up to 30 MB. Extra documents accept a broader list but cap each file at 10 MB. There is currently no way to delete application documents via the API, so filename cleanup and deduplication must happen before load. (developers.pinpointhq.com)
Pinpoint Token Permission Scopes
The Pinpoint API key used for migration must include the following permission scopes. Missing any one of these will produce silent failures or 403 Forbidden errors on specific endpoints:
- Applications — create and update
- Candidates — read and update
- Custom Attributes — read, write, delete
- Custom Fields — read, write, delete
- Documents/Attachments — write (for resume uploads)
Verify with your Pinpoint account administrator that the API key generated has all five scopes before running any migration pipeline. The minimum read scope for candidates is also required to verify email-based deduplication behavior.
Entity-by-Entity Mapping
This is where migrations succeed or fail. Each entity requires deliberate mapping decisions.
Candidates
| JobScore Field | Pinpoint Equivalent | Notes |
|---|---|---|
first_name, last_name |
Candidate name fields | Direct map |
email_addresses [] (personal, work) |
Candidate email | Pinpoint links on primary email; verify single vs. multiple email support in your account |
phone_numbers [] (home, mobile, other) |
Candidate phone | Type mapping required |
address (country, state, city, postal_code, street) |
Candidate location | Direct map |
tags [] |
Candidate tags | Tags must exist in Pinpoint before assignment; system tags cannot be set via API |
profile_links [] (github, linkedin, twitter) |
Social/profile links | Type normalization needed |
employment [] |
Work experience | Nested employer → positions structure; map to Pinpoint's format |
education [] |
Education history | Degree type normalization needed (bachelors, masters, doctorate, etc.) |
source_type + source_detail |
channel + channel_source |
Map using the 12-source-type table above |
Candidate Email Collision Behavior
When you POST a candidate create to Pinpoint and the email already exists in the system, Pinpoint's behavior depends on your account configuration and whether you're creating a candidate directly or via an application. The safest approach for migration:
- Before loading any candidate, query
GET /candidates?filter [email]={email}to check for existing records. - If a match exists, capture the existing Candidate ID and use it in your application create payload — do not attempt a second candidate create.
- If no match exists, create the candidate and capture the new Candidate ID.
This pre-check adds one API call per candidate but eliminates the ambiguity of relying on collision behavior that may vary by account. At Pinpoint's 120 requests/minute limit, pre-checking 10,000 candidates adds approximately 83 minutes to extraction — budget for it.
Applications and Stage Mapping
Both systems are application-centric: a candidate applies to a job, creating an application record. The mapping challenge is in workflow stages and statuses.
JobScore application statuses: new, active, declined, withdrawn, hired. The active status is paired with a workflow stage (e.g., "Schedule Recruiter Screen", "Onsite Interview", "Offer"). Stages are customizable per job.
Pinpoint also uses configurable pipeline stages per job. Your migration must:
- Extract all workflow stages from each JobScore job via
GET /v2/jobs/{job_id}/workflow_stages - Create corresponding stages in Pinpoint manually (since
POST /jobsand stage creation are UI operations) - Translate JobScore's
status+stagecombination into Pinpoint's stage model - Preserve disposition codes for declined/withdrawn applications
Build a stage-mapping table before writing any migration code. Export all unique stage names across all JobScore jobs. Many companies use similar names ("Phone Screen", "Onsite", "Offer") but the IDs are completely different — and Pinpoint stages are also per-job.
Do not build one global string-to-string stage map. Build a crosswalk by requisition family or by exact source workflow. A JobScore stage like "Phone Interview" may map to Pinpoint "Interview" for one business unit and to "Recruiter Screen" for another.
stage_map:
sales:
New: Applied
Screening: Screening
Interviewing: Interview
Offer: Offer
engineering:
Schedule Phone Interview: Recruiter Screen
Phone Interview: Technical Screen
Interviewing: OnsiteSource Attribution
See the 12 JobScore source types table above for the complete mapping to Pinpoint's channel enum. Key implementation notes:
- JobScore's
referraltype includes referrer name and email insource_detail— preserve both inchannel_sourceas a formatted string:"Referral: Jane Smith (jane@company.com)" networkandreferralboth map to Pinpoint'sreferralchannel; usechannel_sourceto distinguish themsearch_firmmaps torecruiter; preserve the agency name inchannel_source- Any source type not in Pinpoint's enum must fall back to
undeterminedwith the original value stored inchannel_source
Documents and Resumes
JobScore's document model supports types including primary_resume, resume, cover_letter, offer_letter, note_attachment, email_attachment, signed_offer_letter, and other. Each document has metadata (filename, content type, file size) and can be downloaded as binary.
The migration workflow:
- List all documents per candidate:
GET /v2/candidates/{candidate_id}/documents - Download each binary:
GET /v2/documents/{document_id}/download - Store in a secure staging environment (e.g., encrypted S3 bucket with server-side encryption and access logging)
- Validate file type matches extension — a PDF with a
.docextension will fail Pinpoint's validation - Re-upload to Pinpoint via its attachment endpoints, linked to the specific Application
This is the most time-consuming part from an API-call perspective. A candidate with 5 documents requires 6 API calls to JobScore (1 list + 5 downloads) plus corresponding uploads to Pinpoint. At 3,600 GET requests/hour, a 10,000-candidate dataset with an average of 3 documents each means ~40,000 GET requests — roughly 11 hours of extraction at max throughput.
Since Pinpoint does not allow deleting application documents via the API, cleanup and deduplication must happen in staging before any upload. There is no undo.
Candidate resumes contain PII. Never store downloaded documents on unencrypted local drives during migration. Ensure your staging environment complies with GDPR and CCPA requirements, including access controls, encryption at rest, and time-bounded retention of the staging copies.
Notes and Interview Feedback
JobScore's notes endpoint returns both plain notes and interview feedback. Interview feedback includes named ratings with numeric scores (1.0–4.0 scale). Example: "Overall Recommendation: 2.5", "Communication skills: 3.2".
Pinpoint handles evaluation differently — it uses structured scorecards and custom attributes. There's no 1:1 mapping for JobScore's rating format. Your options:
- Flatten ratings into note text. Append the rating name and score to the note's HTML content. Preserves the data but loses the structured, queryable nature.
- Map to Pinpoint custom attributes. If you've configured equivalent scoring criteria, map the ratings to those fields. More work but preserves data quality.
- Store as metadata. A fallback for teams that don't need granular feedback history in the new system.
Option 1 is what most migrations use. It's pragmatic and avoids data loss.
JobScore's List Notes endpoint excludes private notes by default. You must pass include_private=true to extract them. Miss this parameter and you silently drop feedback that hiring managers marked as confidential.
Also watch for rich-text formatting. JobScore notes can include HTML (bold, lists, links). Injecting this directly into Pinpoint may break rendering. Sanitize HTML tags, converting them to the markdown or HTML subset that Pinpoint's UI expects. Test rendering with a representative sample of note content before full load.
EEO Data
JobScore provides EEO data (gender, race, veteran status, disability) via a dedicated /v2/eeo endpoint. This data is compliance-sensitive:
- Must be migrated separately from candidate profile data
- Access should be restricted to authorized personnel only, with separate API credentials if possible
- GDPR and CCPA considerations apply — ensure you have legal basis for the transfer and that the staging environment for EEO data is separately access-controlled
- Map demographic data strictly to Pinpoint's designated EEO/diversity fields, never into custom text fields or note content
If you're migrating into a Pinpoint instance with blind recruiting active, ensure your API payloads don't inadvertently bypass privacy filters. Confirm with Pinpoint support which fields are screened by blind recruiting mode before you define your EEO target schema.
For compliance guidance, see our GDPR and CCPA compliance guide.
Custom Fields
Pinpoint custom fields are typed and tied to a resource type (application, candidate, job, job_seeker, or offer). Custom attributes must match the field's declared type, and Pinpoint allows only one custom attribute for a given field/resource pair. (developers.pinpointhq.com)
JobScore exposes job_field_answers [] with typed values (text, number, checkbox, date, multiple_choice, compensation_range, currency). Type conversion requirements:
| JobScore type | Pinpoint custom field type | Notes |
|---|---|---|
text |
text |
Direct map |
number |
number |
Verify decimal handling |
checkbox |
boolean |
Map true/false |
date |
date |
Confirm UTC handling |
multiple_choice |
select |
Pre-create options in Pinpoint |
compensation_range |
text or split into two number fields |
No native range type in Pinpoint |
currency |
number |
Convert from cents; verify currency unit |
Define the target schema before the first pilot load. A 422 Unprocessable Entity on a custom attribute type mismatch during a 10,000-record import is expensive to diagnose mid-run.
Migration Approaches: Trade-offs
Full API-to-API Migration
Best for: Companies with JobScore Enterprise plans and datasets larger than a few hundred candidates.
Extract all entities from JobScore via the Hire API v2. Transform the data to match Pinpoint's JSON:API schema. Load into Pinpoint via its write endpoints.
What drives timeline variance (2–4 weeks for 5,000–20,000 candidates):
| Variable | Low end | High end |
|---|---|---|
| Documents per candidate | <1 average | 5+ average |
| Custom field count | <10 fields | 30+ fields, complex types |
| Stage complexity | 1–2 job families | 10+ job families with unique stages |
| Historical depth | 12 months | 5+ years |
| Deduplication complexity | Clean email data | Multiple duplicates, email changes over time |
A dataset of 10,000 candidates with low document volume and 2 job families can complete extraction + loading in 3–4 days of automated processing. The same dataset with 5 documents per candidate average, 25 custom fields, and 8 job families may take 2–3 weeks including mapping, pilot validation, and delta passes.
Pros:
- Preserves full relational structure
- Migrates documents, notes, feedback, EEO data
- Maintains candidate-application-job relationships
- Scriptable, repeatable, auditable
Cons:
- Requires JobScore Enterprise plan
- Rate limits constrain throughput
- Document migration is time-intensive
- Jobs must be created manually in Pinpoint UI before import
CSV Export + Manual Supplement
Best for: Small datasets (under 500 candidates) where API access is unavailable or budget-constrained.
Export candidate lists via JobScore's CSV export. Use Pinpoint's self-service CSV import (job-by-job, files over 50 candidates should be split). Manually re-add documents candidate by candidate. (help.pinpoint.support)
Pros:
- No Enterprise plan required for extraction
- Faster initial setup
Cons:
- Loses relational structure, stage history, feedback
- No documents, no notes, no EEO data in the CSV
- Manual reconciliation required
- Not suitable for compliance-sensitive migrations
Pinpoint's self-service import is a controlled, job-by-job upload — not a full historical migration tool. Pinpoint's own docs state that documents typically need to be re-added manually after CSV import. (help.pinpoint.support)
Hybrid: API for Active Data, Archive for History
A hybrid plan moves active jobs, recent candidates (typically 12–24 months), open offers, and anything with documents or feedback through the APIs. Long-tail history gets archived separately as structured JSON exports plus raw document binaries — stored as NDJSON (newline-delimited JSON) for candidate/application records and a ZIP archive of document binaries organized by candidate_id/document_id/filename. Compliance-oriented teams can use JobScore's OFCCP Applicant Log for defensible retention of older records.
This works well when operations need live, searchable recruiting data in Pinpoint, but legal or audit stakeholders mainly need defensible retention for older records. Define the cutoff date in writing, signed off by legal, before starting.
Managed Migration Service
Best for: Teams that need full fidelity without dedicating engineering time to a one-time data problem.
A migration partner handles extraction, transformation, and loading — including document re-uploads, per-job stage mapping, EEO data isolation, and the edge cases your internal team would be solving for the first and only time.
The Correct Ingestion Sequence
The order you push data into Pinpoint matters. A 404 Not Found on a relationship ID is the most common sequencing error, and it happens when you try to create an application before the referenced candidate or job exists.
- Create Jobs in Pinpoint UI. Since Pinpoint's public API only supports job updates, not job creation, all jobs must be manually created in the Pinpoint interface first. Export all JobScore jobs to a structured reference sheet (title, requisition number, stages). Create corresponding Pinpoint jobs and capture the resulting Pinpoint Job IDs into your mapping table.
- Create pipeline stages per job. Within each Pinpoint job, create the stages that match your per-job stage crosswalk. Capture stage IDs.
- Create Candidates via API. Push candidate profile data (name, email, phone). Pre-check for email collisions. Capture the new Pinpoint Candidate IDs.
- Create Applications via API. Link Candidate ID to Job ID. Set
applied_atwith historical timestamps. Setskip_notifications_on_create: trueto prevent Pinpoint from emailing candidates and internal users during migration. (developers.pinpointhq.com) - Upload Documents. Push resumes and cover letters, linking to the specific Application ID. Validate file types before upload.
- Append Notes & History. Push historical activities, interviewer notes, and stage changes.
{
"data": {
"type": "applications",
"attributes": {
"applied_at": "2023-10-15T14:30:00Z",
"skip_notifications_on_create": true
},
"relationships": {
"candidate": {
"data": { "type": "candidates", "id": "cand_98765" }
},
"job": {
"data": { "type": "jobs", "id": "job_12345" }
}
}
}
}Rate Limit Math: Planning Your Migration Window
The rate-limit asymmetry between the two systems dictates your migration timeline.
JobScore extraction (GET): 3,600 requests/hour = 60 requests/minute. With 100 records per page, you can extract ~6,000 candidate records per hour from the candidates endpoint alone. But each candidate requires additional calls for applications, documents, notes, and EEO data. Budget 5–8 API calls per candidate for full extraction.
Pinpoint ingestion (write): ~120 requests per minute (IP-based). Sustained write throughput is roughly 30 requests/minute if you account for retries and the 240/8-minute ceiling.
For a 10,000-candidate migration with an average of 3 documents each:
| Operation | API calls | Rate limit | Hours |
|---|---|---|---|
| JobScore candidate extract | 100 pages × ~100 candidates | 3,600 req/hr | ~2.8 hrs |
| JobScore document list + download | ~40,000 calls | 3,600 req/hr | ~11 hrs |
| JobScore notes + EEO extract | ~20,000 calls | 3,600 req/hr | ~5.5 hrs |
| Pinpoint candidate pre-check | 10,000 calls | ~1,800 req/hr | ~5.5 hrs |
| Pinpoint application + doc upload | ~40,000 calls | ~1,800 req/hr | ~22 hrs |
| Total (sequential) | ~110,000 calls | — | ~47 hrs |
Run the extraction and loading in parallel pipelines with an intermediate staging database. Don't extract and load in the same loop — one rate limit error shouldn't block the other pipeline. With parallel execution, total wall-clock time for this dataset is approximately 24–30 hours of continuous processing, not counting mapping setup and validation.
Webhook Availability for Delta Migrations
For incremental delta passes (re-syncing records updated since initial extraction), webhooks are more reliable than polling. JobScore supports webhooks for candidate, application, and job events — subscribe to candidate.updated and application.updated events during the migration window to capture changes without burning GET rate limit budget on polling. Confirm webhook endpoint availability in your JobScore plan. Pinpoint does not currently expose outbound webhooks for migration use, but its API supports date-range filtering for delta queries.
Edge Cases That Break Migrations
Duplicate Candidates
JobScore explicitly states there is no uniqueness validation for candidates added through the API. Duplicates accumulate — John Doe applies with Gmail in 2019 and Yahoo in 2022. Pinpoint collates candidate profiles by email match. De-duplicate in your staging environment using a composite key (normalized email + name + phone) before loading into Pinpoint. Pre-merging in staging is more predictable than relying on either system's native dedup.
After migration, if you discover duplicates in Pinpoint, the manual merge feature in the UI preserves one master profile and attaches the other's applications to it. However, the API consequences of a merge — which Candidate ID persists, whether external system IDs on child applications are preserved — should be verified before relying on post-migration merge as a cleanup strategy.
Per-Job Workflow Stages
JobScore workflow stages are customizable per job. Job A might have 9 stages; Job B might have 5 completely different stages. You cannot assume a universal stage mapping. Query GET /v2/jobs/{job_id}/workflow_stages for every job and build a per-job mapping table.
Historical Stage Timestamps
Recruiting teams rely on "Time in Stage" reports. If you import all applications today, Pinpoint timestamps them with today's date. You must explicitly pass historical applied_at and stage transition timestamps. Verify Pinpoint's current endpoint capabilities for historical overrides before promising stakeholders that historical velocity metrics will be preserved.
Pinpoint System Tags
System tags like Internal, Duplicate, Referral, and External Recruiter cannot be set through Pinpoint's API. Only custom tag contexts should be part of your mapping plan. Attempting to assign system tags via the API will either silently fail or return a validation error. (developers.pinpointhq.com)
Notification Suppression
Pinpoint's application create endpoint triggers both applicant and internal notifications by default. Migration loads must use skip_notifications_on_create: true to prevent thousands of spurious emails to candidates and hiring managers. Verify this parameter is present on every application create call in your script — not just the first one. (developers.pinpointhq.com)
Document Validation and Cleanup
Mismatched content types (e.g., a PDF with a .doc extension) will fail Pinpoint's validation with a 422 error. Validate file type headers before upload using a library that inspects magic bytes, not just file extensions. Since Pinpoint does not allow deleting application documents via API, any document that gets uploaded incorrectly persists until manually removed from the UI.
Incomplete Application Validation
Pinpoint's application create endpoint does not validate that required job-question answers are present. You can create incomplete historical applications unless your importer validates required answers before load. This means records that look correct on import may flag as incomplete when a recruiter opens them in the UI. (developers.pinpointhq.com)
Offer Field Mapping
JobScore offers store custom field values as name-value string pairs with no standard schema — field names like "Compensation Amount", "Sign On Bonus", "Relocation Assistance" are all employer-configured. Your migration must handle arbitrary offer fields and map them to Pinpoint's offer model. Inventory all offer field names in your JobScore instance before designing the offer mapping.
Compensation Format
JobScore stores salary in cents with separate public/private ranges. Pinpoint uses its own compensation model. The conversion is: pinpoint_amount = jobscore_cents / 100. Confirm the unit and visibility mapping before you end up posting an annual salary of $7,500,000 instead of $75,000. Include a validation check that flags any converted compensation value outside a defined reasonable range (e.g., $10,000–$2,000,000 annually) before load.
Pre-Migration Runbook
- Confirm API access. Verify your JobScore Enterprise plan includes API access. Generate a Personal Access Token at Integrations > JobScore API & Webhooks. Generate a Pinpoint API key with Application, Custom Attributes, Custom Fields, Candidates, and Attachments permissions. Verify all five scopes before proceeding.
- Inventory your data. Count candidates, applications, documents, notes, custom fields, and distinct job families. Use the rate-limit table above to calculate total migration time.
- Build per-job stage mapping tables. Export all workflow stages from every JobScore job via
GET /v2/jobs/{job_id}/workflow_stages. Map to Pinpoint pipeline stages. Document the crosswalk in a versioned spreadsheet. - Map the 12 source types. Use the source-type table in this guide. Confirm
channel_sourceformat for referrals and search firms with your reporting team. - Create jobs and stages in Pinpoint UI. Since job creation is not available via API, create all target jobs manually. Capture and store Pinpoint Job IDs and Stage IDs in your mapping table before any API work begins.
- Define custom field schema. Create target custom fields, tag categories, and structured sections in Pinpoint before any pilot. Review the type conversion table above. Pinpoint's custom attribute typing and tag-context rules make late-stage improvisation expensive.
- Handle EEO data separately. Isolate compliance data extraction and loading. Restrict access. Confirm target fields with Pinpoint before writing EEO migration logic.
- De-duplicate candidates. Query by email and composite keys (name + phone) before creating records in Pinpoint. Build and run the dedup pass against your staging database before any Pinpoint loads.
- Define cold archive format. Before starting extraction, decide on the archive format: NDJSON for structured records, ZIP of binaries organized by
candidate_id/document_id/filename. Document retention policy and access controls. - Run a dry run with ugly data. Migrate 25–100 candidates that include multi-application candidates, hires, declines, offers, attachments, private notes, and source oddities. Validate the resulting Pinpoint candidate profiles — not just row counts. Check rendering of HTML notes, compensation values, and custom field display.
- Load production data with throttling. Suppress Pinpoint notifications with
skip_notifications_on_create: trueon every application create. Use adaptive backoff on429responses, reading theRetry-Afterheader. Log all422errors with full response body for post-run diagnosis. - Run a delta pass before cutover. Re-extract records updated since the initial snapshot using
updated_afteron JobScore endpoints. Import the delta. Freeze recruiter changes briefly. Reconcile counts and spot-check both sides. - Retain a cold archive. Keep structured NDJSON exports of all JobScore data plus document binaries in a separately secured environment even after Pinpoint becomes the system of record. This is your compliance backstop.
For a full compliance-focused checklist, see our ATS data migration checklist.
Making the Right Call
JobScore to Pinpoint is a tractable migration — both systems use a candidate-application-job model, both have workable APIs, and there are no exotic data structures on either side. The difficulty is in the details: per-job stage mapping, source attribution translation across 12 source types to 11 channel values, document re-upload at scale, the absence of a POST /jobs endpoint requiring manual job creation, and the rate-limit arithmetic that determines whether your migration takes 2 days or 2 weeks.
If you're moving a handful of open reqs and only need current applicants under 500 candidates, CSV may be enough. If you need notes, feedback, attachments, EEO data, or multi-application history, the spreadsheet export won't get you there.
The teams that get this right: map all 12 source types before writing code, build per-job stage crosswalks before touching the API, pre-check for email collisions before creating candidates, and run a real dry run with production data — including multi-application candidates, private notes, and offer records — before committing to a go-live date.
Frequently Asked Questions
- Does JobScore have an API for data migration?
- Yes. JobScore offers a Hire API v2 at api.jobscore.com/v2, but it is restricted to Enterprise customers and select partners. The API supports extracting candidates, applications, jobs, documents, notes, EEO data, and offers. Non-Enterprise plans are limited to CSV exports which lose relational data and attachments.
- Can I migrate from JobScore to Pinpoint with CSV only?
- Only for small, low-fidelity moves. JobScore's Excel export covers basic fields like name, email, status, and source. Pinpoint's self-service import is job-by-job CSV with documents typically re-added manually. You lose interview feedback, notes, attachments, stage history, and EEO data.
- What are the rate limits for JobScore and Pinpoint APIs?
- JobScore allows 3,600 GET requests/hour and 400 write requests/hour with a 20 request/minute burst cap. Pinpoint allows roughly 120 requests per IP per minute and 240 per 8 minutes. For a 10,000-candidate migration with documents, expect 2–3 days of continuous processing.
- Will Pinpoint email candidates when I import historical data?
- Yes, by default. Pinpoint's application create endpoint triggers both applicant and internal notifications. Migration loads must use skip_notifications_on_create: true to prevent thousands of spurious emails.
- How do I handle interview feedback from JobScore in Pinpoint?
- JobScore stores interview feedback as notes with named ratings on a 1.0–4.0 scale. Pinpoint uses a different evaluation model with structured scorecards. The most practical approach is to flatten rating names and scores into the note's HTML content during migration, preserving the data without loss.