Skip to navigation

2026-09-17 — Contact Profile Refresh Requests: change-detection fields (ds_*)

Added

ds_* fields in response_format=changes

GET /api/v1/contact_profile_refresh_requests/request_status?response_format=changes now returns, per profile, a compact change-detection block instead of the full contact_data:

  • Position — ds_position_state, ds_current_position_count, ds_primary_company, ds_primary_title, ds_primary_start_date, ds_position_primacy_confidence.
  • Baseline — ds_baseline_primary_company, ds_baseline_primary_title (what the verdict was compared against; null on the first refresh and when the previous refresh found no current position), ds_first_seen.
  • Verdict — ds_company_change, ds_title_change, ds_job_changes, ds_company_change_type, ds_title_change_type, ds_change_events, ds_change_description, ds_change_status.

The ds_* keys are omitted (not null) while no verdict is available for the profile — the refresh hasn’t concluded for the profile yet, no contact was matched, or the profile was processed before this release. first_name / last_name / linkedin_url in this format are the contact’s identity at the time the verdict was computed, not the values you submitted.

See Contact Profile Refresh Requests → response_format=changes for the field-by-field reference.

Webhook documented

The webhook now has its own entry in the API Reference, Contact Profile Refresh Request completed: a bare JSON array of { custom_columns, contact_data } for refreshed profiles, followed by a second POST with the not-found profiles. No envelope and no signature; a delivery that fails is sent again the next time you call request_status, and further calls can trigger additional deliveries. See Webhook payload for delivery and idempotency guidance.


Changed

changed_company / changed_title now compare the primary position with the previous snapshot

In the full format and the webhook payload, changed_company and changed_title used to mean “the set of employers changed inside a look-back window”. They now answer a single question: did the contact’s primary employer / title change compared with your account’s previous refresh of this contact? They are the same values as ds_company_change / ds_title_change, computed once, when the refresh concludes for that profile, and never recomputed.

Consequences for clients:

  • The first refresh of a contact for your account reports false / false. If the contact already had positions, ds_change_status is undetermined with reason no prior snapshot; if this refresh delivered the contact’s first positions, both change types are newly_added (no_change when those positions have all already ended). It establishes the baseline; the second refresh is the first that can report a change.
  • There is no look-back window any more — contact_refresh_time doesn’t limit how far back the comparison reaches; it decides which contacts get refreshed.
  • A move inside a corporate family, a renamed employer, or a role that ends and resumes at the same employer never sets changed_company.

Documentation corrections

  • synchronous defaults to true and never blocks on enrichment: the call returns every profile inline, and stale / unmatched contacts are refreshed in the background either way.
  • contact_refresh_time is omitted for the 30-day default (the request then echoes null).
  • request_status accepts response_format (summary / changes); page_no / page_size default to 1 / 50 and start at 1. The paginated wrapper echoes both values verbatim (as strings when supplied in the query).
  • The full-format response is the Contact Profile Refresh Request (id, unique_identifier, webhook, status, contact_refresh_time, user) plus its profiles in contact_profile_refreshes — a flat array for synchronous requests, { total, page_no, page_size, profiles } for asynchronous ones.
  • contact_data always carries the same 33 keys; enrichment_status and logo_url live inside it. Emails and phones are never part of it. profile_completeness is a label (complete … poor), contact_education an array of objects, current_company_and_title an object ({} when none) and previous_companies_and_titles an array ([] when none) — both snapshots that may predate the refresh, not the refreshed state. search_status can be null on a profile closed without a final status.
  • New request fields documented: api_key in the body, find_match_and_refresh, requested_attributes, and the profile field metadata.
  • Pricing for API requests is tiered (0.25 / 1.00 / 1.25 / 1.50 per completed profile with a matched contact, up to the ceiling set by requested_attributes).
  • 401 responses (invalid key, inactive account, expired subscription) and the full 422 surface are now in the spec.
  • Credit metrics: daily_profile_request_metrics counts profile rows on the server date, not requests at midnight in the user’s timezone.

Removed

Beta changes[] key

The beta changes[] array in response_format=changes is gone. Use ds_change_events and the ds_* booleans instead.


Notes for clients

  • If you consume changed_company / changed_title for job-change alerts, expect false / false on every contact’s first refresh after this release and treat the second refresh as the first meaningful signal.
  • In the changes format, key off the presence of ds_* keys and ds_change_status before reading the booleans — absent keys mean no verdict is available (not yet, or not at all, for example for an unmatched profile), and undetermined means “no verdict”; neither means “no change”.
  • Correlate webhook payloads through custom_columns (put your own ID in metadata); the payload carries no request identifier.