---
title: "Make person primary"
method: POST
path: "/api/people/{sid}/make-primary"
tags: ["people"]
---

# Make person primary

`POST /api/people/{sid}/make-primary`

## Request

- Base URL: `https://beta.getsales.io/playtables/v1`
- URL: `https://beta.getsales.io/playtables/v1/api/people/{sid}/make-primary`
- Auth: HTTP bearer and API key in header `Team-SID`

Declare a row flagged duplicate the primary of its group: the flag and the pointer swap, this row goes back to ok (or no_contact_data), the row it duplicated becomes the duplicate pointing here, every other copy is re-pointed here. Nothing is merged or deleted; reversible: call it on the other row. A row not flagged answers already_primary true. When NOT: removing a copy (delete_person); asking which is primary (duplicate_of_sid on get_person).

Contract:
- MCP tool `make_person_primary`, registry package `mcp.playtables/people`, mount `playtables.people`.
- Operation `action`, response envelope `action`.
- Flags: none.

## Path parameters

- `sid` string, required: The flagged duplicate that is the real record.

## Response `200`

`action` success envelope.

- MakePersonPrimaryResponse
  - `success` true, required
  - `operation` 'action', required
  - `action` string, required: kebab-case verb; matches the route segment.
  - `item` object, nullable, required
    - `sid` string, required
    - `team_sid` string, required
    - `playtable_sid` string, required: pt_tb_… - the SINGLE playtable membership; service-managed, changes only via move (create on_duplicate:move / run type move).
    - `data_source_sid` string, nullable, required
    - `data_source_item_sid` string, nullable, required
    - `company_sid` string, nullable, required
    - `context` object, nullable, required: DERIVED and read-only (CONTEXT_FIELDS_RESEARCH.md section 5): the name, first name, picture, role, headline and company a surface or a model shows next to the row, resolved at read from the observations ledger: a manual word until reset, then the newest LinkedIn-derived observation, then any other. The only place these words are: the row keeps identifiers (phase E, 2026-10-22). Null only on an event payload.
      - `name` string, nullable, required
      - `first_name` string, nullable, required: The source's own first name when it gave one, else the first word of name.
      - `picture_url` string, nullable, required: A permanent copy over a wire link, the wire link that lasts longest, an expired one counts as none.
      - `role` string, nullable, required: The title at the company (the Sales Navigator wire's position, the enrichment's).
      - `headline` string, nullable, required: The profile line (the regular and Recruiter wires, the inbox participant, the enrichment).
      - `company` object, nullable, required
        - `sid` string, nullable, required: cr_co_… when the person is linked to an account (the account's own resolved context follows); null = a name that is not an account, an import whose company column resolved to none.
        - `name` string, nullable, required
        - `domain` string, nullable, required
        - `picture_url` string, nullable, required
        - `ln_id` string, nullable, required: The company's LinkedIn id: the linked account's own, else the person's company id as a source or an edit gave it.
      - `sources` object, required: Per resolved key, where it came from and when.
    - `ln_member_id` string, nullable, required
    - `ln_id` string, nullable, required
    - `sn_id` string, nullable, required
    - `recruiter_id` string, nullable, required
    - `work_email` string, nullable, required
    - `personal_email` string, nullable, required
    - `work_phone_number` string, nullable, required
    - `personal_phone_number` string, nullable, required
    - `status` 'ok' | 'failed_validation' | 'no_contact_data' | 'duplicate' | 'stoplisted', required: Service-managed row validity (import validation, stoplist, duplicate detection) - not client-writable.
    - `duplicate_of_sid` string, nullable, required: While status is duplicate: the PRIMARY row (cr_pp_…) this one is a copy of - one primary per group, re-derived on every recheck, swapped by make_person_primary. Null otherwise.
    - `duplicate_matched_kind` 'ln_member_id' | 'ln_id' | 'sn_id' | 'recruiter_id' | 'email' | 'ln_nickname' | 'phone', nullable, required: While status is duplicate: the identifier the check matched on. Null otherwise.
    - `linkedin_status` 'need_enrichment' | 'enrichment_failed' | 'no_data' | 'blocked' | 'ok', required
    - `email_status` 'need_search' | 'no_data' | 'bounced' | 'ok' | 'catch_all' | 'unverified', nullable, required
    - `phone_status` 'need_search' | 'no_data' | 'ok', nullable, required
    - `tag_sids` string[], nullable, required
    - `external_refs` union, required: Free-form CRM sync map (hubspot_contact_id, …) - keys integration-defined, round-tripped as-is; PHP empty map serialises as [].
      - object
      - unknown[]
        - unknown
      - unknown
    - `last_enrich_at` string, nullable, required
    - `created_at` string, required
    - `updated_at` string, required
    - `deleted_at` string, nullable, required
    - `pipeline_stage_sid` string, nullable, required: The funnel stage the person is in (cr_ps_...), derived from activity or moved by hand (pipeline_stages.md); null = no stage.
  - `result` object, required
    - `already_primary` boolean, required: true = the row was not flagged; nothing changed.
    - `previous_primary_sid` string, nullable, required: The row that was the primary and is the duplicate now (cr_pp_…); null when the row was simply lifted.
    - `repointed_count` integer, required: Other copies of the old primary now pointing at this row.
  - `meta` object, required
    - `trace_id` string, required: UUID v7; same 128-bit value as the X-Trace-Id header.
    - `span_id` string, required: 16 hex chars, root span of this request.
    - `timestamp` string, required: ISO 8601 UTC (Y-m-dTH:i:sZ), response time.
    - `duration_ms` integer, required: Server-side wall clock.
    - `team_sid` string, nullable: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends.
    - `actor_type` string, nullable: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends.

## Other responses

- `4XX`: MCP error envelope. `error.code` is one of validation_failed, nothing_to_update, not_found, relation_not_found, invalid_transition, limit_exceeded, payment_required, duplicate_rejected, conflict, delete_blocked, unauthorized, forbidden, rate_limited, not_implemented.
- `5XX`: MCP error envelope with `error.code` internal_error or service_unavailable.

---

[API](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables.md) - [All operations](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables/llms.txt) - [OpenAPI document](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables/revisions/aa394506515d?raw)
