2026-09-17 — Contact Profile Refresh Requests: change-detection fields (ds_*)
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;nullon 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_statusisundeterminedwith reasonno prior snapshot; if this refresh delivered the contact’s first positions, both change types arenewly_added(no_changewhen 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_timedoesn’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
synchronousdefaults totrueand never blocks on enrichment: the call returns every profile inline, and stale / unmatched contacts are refreshed in the background either way.contact_refresh_timeis omitted for the 30-day default (the request then echoesnull).request_statusacceptsresponse_format(summary/changes);page_no/page_sizedefault 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 incontact_profile_refreshes— a flat array for synchronous requests,{ total, page_no, page_size, profiles }for asynchronous ones. contact_dataalways carries the same 33 keys;enrichment_statusandlogo_urllive inside it. Emails and phones are never part of it.profile_completenessis a label (complete…poor),contact_educationan array of objects,current_company_and_titlean object ({}when none) andprevious_companies_and_titlesan array ([]when none) — both snapshots that may predate the refresh, not the refreshed state.search_statuscan benullon a profile closed without a final status.- New request fields documented:
api_keyin the body,find_match_and_refresh,requested_attributes, and the profile fieldmetadata. - 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). 401responses (invalid key, inactive account, expired subscription) and the full422surface are now in the spec.- Credit metrics:
daily_profile_request_metricscounts 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_titlefor job-change alerts, expectfalse/falseon every contact’s first refresh after this release and treat the second refresh as the first meaningful signal. - In the
changesformat, key off the presence ofds_*keys andds_change_statusbefore reading the booleans — absent keys mean no verdict is available (not yet, or not at all, for example for an unmatched profile), andundeterminedmeans “no verdict”; neither means “no change”. - Correlate webhook payloads through
custom_columns(put your own ID inmetadata); the payload carries no request identifier.