---
title: "Set Deal Contact Role"
method: PUT
path: "/deals/{deal_id}/contacts/{contact_id}"
tags: ["Deals"]
---

# Set Deal Contact Role

`PUT /deals/{deal_id}/contacts/{contact_id}`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/deals/{deal_id}/contacts/{contact_id}`
- Auth: HTTP bearer

Set (or clear) a contact's buying-committee role on a deal.

The role lives on the deal-contact LINK (`cb_deal_contacts.role`), not on
the contact: the same person can be a champion on one deal and an end user
on another. This endpoint only re-roles an EXISTING link — it never
creates one, so attaching and roling stay separately auditable. Use
``PATCH /deals/{deal_id}`` with ``add_contact_ids`` (optionally alongside
``contact_roles``) to link a contact first.

``{contact_id}`` is the ``mashup_contact_id``, the same integer id this API
uses for a contact everywhere else (``GET /contacts/{id}``, the ``id`` on
every ``ContactBrief``). The internal app route calls the identical value
``{person_id}``; that name is a historical misnomer and is not repeated
here.

Body is ``{"role": "champion"}``; an explicit ``{"role": null}`` clears the
role. ``role`` is required — an empty body is a 422, because a PUT that
names no new state for the sub-resource is a caller bug, and reading
omission as "clear" would destroy data on a typo.

Returns the updated contact. 404 when the deal does not exist in this org
or the contact is not linked to it; 422 on a role outside the vocabulary.

## Path parameters

- `deal_id` string, required: Deal ID (UUID)
- `contact_id` integer, required: mashup_contact_id (BigInt) of a contact already linked to this deal

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Request body

- DealContactRoleUpdateRequest: Body for `PUT /deals/{deal_id}/contacts/{contact_id}`. `role` is REQUIRED but nullable. That combination is the whole point of a PUT here: `{"role": "champion"}` sets it, `{"role": null}` clears it, and an empty body `{}` is a 422 rather than a silent no-op — a PUT that says nothing about the sub-resource it is replacing is a caller bug, and the alternative (treating omission as "clear") would delete data on a typo.
  - `role` string, nullable, required: Buying-committee role for this contact on this deal. One of: champion, decision_maker, influencer, blocker, coach, end_user. Pass null to clear the role.

## Response `200`

Successful Response

- ApiResponseContactBrief
  - `data` ContactBrief, required: Lightweight contact summary embedded in deal responses. `id` is the canonical `mashup_contact_id` (BigInt), matching how contacts are addressed everywhere else in the public API (g8_search_contacts, g8_lookup_person, etc.). `role` is the contact's role within THIS deal (champion / decision_maker / influencer / blocker / coach / end_user) and is null when no role was set.
    - `id` integer, required
    - `name` string, nullable
    - `email` string, nullable
    - `title` string, nullable
    - `role` string, nullable
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
