Skip to content

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.

Abdul Aleem Abdul Aleem · · 23 min read
JobScore to Pinpoint Migration: APIs, Mapping & Pitfalls
TALK TO AN ENGINEER

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

(developer.jobscore.com)

Warning

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/eeo endpoint
  • 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

(support.jobscore.com)

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

(developers.pinpointhq.com)

Warning

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.

Info

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 a Retry-After header 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"}}]}. The source.pointer field identifies the specific failing attribute. Common causes: invalid channel enum 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:

  1. Before loading any candidate, query GET /candidates?filter [email]={email} to check for existing records.
  2. If a match exists, capture the existing Candidate ID and use it in your application create payload — do not attempt a second candidate create.
  3. 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:

  1. Extract all workflow stages from each JobScore job via GET /v2/jobs/{job_id}/workflow_stages
  2. Create corresponding stages in Pinpoint manually (since POST /jobs and stage creation are UI operations)
  3. Translate JobScore's status + stage combination into Pinpoint's stage model
  4. Preserve disposition codes for declined/withdrawn applications
Tip

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: Onsite

Source Attribution

See the 12 JobScore source types table above for the complete mapping to Pinpoint's channel enum. Key implementation notes:

  • JobScore's referral type includes referrer name and email in source_detail — preserve both in channel_source as a formatted string: "Referral: Jane Smith (jane@company.com)"
  • network and referral both map to Pinpoint's referral channel; use channel_source to distinguish them
  • search_firm maps to recruiter; preserve the agency name in channel_source
  • Any source type not in Pinpoint's enum must fall back to undetermined with the original value stored in channel_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:

  1. List all documents per candidate: GET /v2/candidates/{candidate_id}/documents
  2. Download each binary: GET /v2/documents/{document_id}/download
  3. Store in a secure staging environment (e.g., encrypted S3 bucket with server-side encryption and access logging)
  4. Validate file type matches extension — a PDF with a .doc extension will fail Pinpoint's validation
  5. 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.

Danger

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:

  1. 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.
  2. Map to Pinpoint custom attributes. If you've configured equivalent scoring criteria, map the ratings to those fields. More work but preserves data quality.
  3. 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.

Warning

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
Warning

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.

  1. 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.
  2. Create pipeline stages per job. Within each Pinpoint job, create the stages that match your per-job stage crosswalk. Capture stage IDs.
  3. Create Candidates via API. Push candidate profile data (name, email, phone). Pre-check for email collisions. Capture the new Pinpoint Candidate IDs.
  4. Create Applications via API. Link Candidate ID to Job ID. Set applied_at with historical timestamps. Set skip_notifications_on_create: true to prevent Pinpoint from emailing candidates and internal users during migration. (developers.pinpointhq.com)
  5. Upload Documents. Push resumes and cover letters, linking to the specific Application ID. Validate file types before upload.
  6. 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

  1. 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.
  2. 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.
  3. 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.
  4. Map the 12 source types. Use the source-type table in this guide. Confirm channel_source format for referrals and search firms with your reporting team.
  5. 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.
  6. 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.
  7. Handle EEO data separately. Isolate compliance data extraction and loading. Restrict access. Confirm target fields with Pinpoint before writing EEO migration logic.
  8. 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.
  9. 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.
  10. 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.
  11. Load production data with throttling. Suppress Pinpoint notifications with skip_notifications_on_create: true on every application create. Use adaptive backoff on 429 responses, reading the Retry-After header. Log all 422 errors with full response body for post-run diagnosis.
  12. Run a delta pass before cutover. Re-extract records updated since the initial snapshot using updated_after on JobScore endpoints. Import the delta. Freeze recruiter changes briefly. Reconcile counts and spot-check both sides.
  13. 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.

More from our Blog