> ## Documentation Index
> Fetch the complete documentation index at: https://help.privy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a contact

> Update an existing contact identified by its unique `id`.
Include one or more fields to change. Field updates and consent changes
can be combined in a single request.

Mutable fields: `first_name`, `last_name`, `email`, `phone_number`, `tags`, `custom_fields`.

`email` and `phone_number` can only be set when the existing value is
`null`. Once populated, they cannot be changed or cleared.

Consent fields: `email_consent`, `sms_consent`. Use these to subscribe,
unsubscribe, or otherwise manage a contact's marketing consent.

When `sms_consent` is set to `subscribed` and the contact transitions to
a confirmed opt-in state, Privy automatically sends a TCPA-required
welcome SMS. Pass `send_welcome_sms: false` to suppress this message if
you have already collected consent outside of Privy.

**Required scope:** `contacts_write`




## OpenAPI

````yaml openapi/privy-api.yaml PATCH /contacts/{id}
openapi: 3.1.0
info:
  title: Privy API
  version: '1.0'
  description: |
    The Privy API lets you programmatically manage your contact list.
    Create, update, unsubscribe, and remove contacts — or retrieve your
    full contact list with filtering and pagination.
servers:
  - url: https://api.privy.com/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Account
    description: Retrieve information about the authenticated Privy account.
  - name: Contacts
    description: Manage your contact list.
  - name: Orders
    description: Report orders placed in your store.
  - name: Events
    description: Ingest custom events to trigger Flows and other automations.
paths:
  /contacts/{id}:
    patch:
      tags:
        - Contacts
      summary: Update a contact by ID
      description: >
        Update an existing contact identified by its unique `id`.

        Include one or more fields to change. Field updates and consent changes

        can be combined in a single request.


        Mutable fields: `first_name`, `last_name`, `email`, `phone_number`,
        `tags`, `custom_fields`.


        `email` and `phone_number` can only be set when the existing value is

        `null`. Once populated, they cannot be changed or cleared.


        Consent fields: `email_consent`, `sms_consent`. Use these to subscribe,

        unsubscribe, or otherwise manage a contact's marketing consent.


        When `sms_consent` is set to `subscribed` and the contact transitions to

        a confirmed opt-in state, Privy automatically sends a TCPA-required

        welcome SMS. Pass `send_welcome_sms: false` to suppress this message if

        you have already collected consent outside of Privy.


        **Required scope:** `contacts_write`
      operationId: updateContactById
      parameters:
        - $ref: '#/components/parameters/ContactIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateContactByIdRequest'
            example:
              first_name: Janet
              tags:
                - vip
                - wholesale
              email_consent: unsubscribed
      responses:
        '200':
          description: Contact updated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Contact'
              example:
                data:
                  id: cus_a1b2c3d4e5f6g7h8
                  first_name: Janet
                  last_name: Doe
                  email: jane@example.com
                  email_consent: unsubscribed
                  phone_number: '+15551234567'
                  sms_consent: subscribed
                  tags:
                    - vip
                    - wholesale
                  custom_fields:
                    loyalty_tier: gold
                  created_at: '2025-01-15T10:30:00Z'
                  updated_at: '2025-04-01T12:15:00Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientScope'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - bearerAuth: []
components:
  parameters:
    ContactIdParam:
      name: id
      in: path
      required: true
      description: The contact's unique identifier (returned as `id` in contact responses).
      schema:
        type: string
      example: cus_a1b2c3d4e5f6g7h8
  schemas:
    UpdateContactByIdRequest:
      type: object
      description: >
        Include one or more fields to update. The contact is identified

        by the `id` path parameter. Field updates and consent changes can be

        combined in a single request.


        Mutable fields: `first_name`, `last_name`, `email`, `phone_number`,
        `tags`, `custom_fields`.


        `email` and `phone_number` can only be set when the existing value is

        `null`. Once populated, they cannot be changed or cleared.


        Consent fields: `email_consent`, `sms_consent`. Use these to manage a

        contact's marketing consent instead of the deprecated unsubscribe
        endpoint.
      properties:
        first_name:
          type: string
          description: Updated first name.
          example: Janet
        last_name:
          type: string
          description: Updated last name.
          example: Smith
        email:
          type: string
          format: email
          description: |
            Sets the contact's email address. Only allowed when the contact's
            current `email` is `null`; once populated, `email` is immutable.
          example: janet@example.com
        phone_number:
          type: string
          description: |
            Sets the contact's phone number in E.164 format. Only allowed when
            the contact's current `phone_number` is `null`; once populated,
            `phone_number` is immutable.
          example: '+15551234567'
        tags:
          type: array
          items:
            type: string
          description: Replaces all existing tags.
          example:
            - vip
            - wholesale
        custom_fields:
          type: object
          additionalProperties:
            type: string
          description: >-
            Replaces all existing custom fields. Must be a flat key-value
            object.
          example:
            loyalty_tier: platinum
        email_consent:
          type: string
          enum:
            - subscribed
            - unsubscribed
            - never_subscribed
            - suppressed
          description: |
            Update the contact's email marketing consent.
            `compliance_suppressed` is read-only — any write to a
            compliance-suppressed contact returns `422`.

            For merchant-suppressed contacts, writing any consent value first
            unsuppresses, then applies the requested transition (e.g.
            `subscribed` unsuppresses then subscribes).
          example: unsubscribed
        sms_consent:
          type: string
          enum:
            - subscribed
            - unsubscribed
            - never_subscribed
            - single_opt_in
          description: >
            Update the contact's SMS marketing consent. `pending` is read-only.

            `subscribed`, `single_opt_in`, and `unsubscribed` require the
            contact

            to have a phone number. `single_opt_in` is idempotent when the
            contact

            is already in that state, and returns `422` from terminal states

            (`pending`, `confirmed`, or `unsubscribed`).
          example: unsubscribed
        send_welcome_sms:
          type: boolean
          default: true
          description: >
            When `sms_consent` is set to `subscribed` and the contact
            transitions

            to a confirmed SMS opt-in state, Privy sends a TCPA-required welcome

            SMS. Set to `false` to suppress this message when you have already

            collected consent outside of Privy.
          example: false
    Contact:
      type: object
      properties:
        id:
          type: string
          description: >-
            Stable, unique identifier for the contact. Use this value in
            `/v1/contacts/{id}` endpoints.
          example: cus_a1b2c3d4e5f6g7h8
        first_name:
          type: string
          example: Jane
        last_name:
          type: string
          example: Doe
        email:
          type: string
          format: email
          example: jane@example.com
        email_consent:
          type: string
          enum:
            - subscribed
            - unsubscribed
            - never_subscribed
            - suppressed
            - compliance_suppressed
          description: >
            Email marketing consent status.


            - `subscribed` — contact opted into email (explicit or implicit
            consent).

            - `unsubscribed` — contact opted out of email.

            - `never_subscribed` — no opt-in and no opt-out.

            - `suppressed` — merchant-suppressed (bounce list, manual action).

            - `compliance_suppressed` — system-suppressed (CAN-SPAM, abuse
            complaint). Read-only.
          example: subscribed
        phone_number:
          type: string
          example: '+15551234567'
        sms_consent:
          type: string
          enum:
            - subscribed
            - unsubscribed
            - never_subscribed
            - single_opt_in
            - pending
          description: >
            SMS marketing consent status.


            - `subscribed` — contact has confirmed SMS opt-in.

            - `unsubscribed` — contact opted out of SMS.

            - `never_subscribed` — no opt-in and no opt-out.

            - `single_opt_in` — merchant collected a single opt-in (not yet
            confirmed).

            - `pending` — Privy is awaiting a confirmation reply. Read-only.
          example: subscribed
        tags:
          type: array
          items:
            type: string
          example:
            - vip
            - repeat-buyer
        custom_fields:
          type: object
          additionalProperties:
            type: string
          example:
            loyalty_tier: gold
            referral_code: JANE2024
        created_at:
          type: string
          format: date-time
          example: '2025-01-15T10:30:00Z'
        updated_at:
          type: string
          format: date-time
          example: '2025-03-20T14:22:00Z'
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - invalid_client
                - invalid_scope
                - unauthorized
                - insufficient_scope
                - not_found
                - conflict
                - validation_failed
                - rate_limited
              example: validation_failed
            message:
              type: string
              example: One or more fields are invalid
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                    example: email
                  message:
                    type: string
                    example: is required
  responses:
    Unauthorized:
      description: Missing or invalid bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: unauthorized
              message: Bearer token is missing or invalid
    InsufficientScope:
      description: Token lacks the required scope for this endpoint.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: insufficient_scope
              message: Token does not have the required scope
    NotFound:
      description: Contact not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: not_found
              message: Contact not found
    ValidationFailed:
      description: One or more fields failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: validation_failed
              message: One or more fields are invalid
              details:
                - field: email
                  message: is required
    RateLimited:
      description: Rate limit exceeded. Retry after the specified time.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
          example: 42
        X-RateLimit-Limit-Minute:
          description: Maximum requests allowed per minute.
          schema:
            type: integer
          example: 60
        X-RateLimit-Remaining-Minute:
          description: Requests remaining in the current minute window.
          schema:
            type: integer
          example: 0
        X-RateLimit-Reset-Minute:
          description: Unix timestamp when the minute window resets.
          schema:
            type: integer
          example: 1711929600
        X-RateLimit-Limit-Day:
          description: Maximum requests allowed per day.
          schema:
            type: integer
          example: 10000
        X-RateLimit-Remaining-Day:
          description: Requests remaining in the current day window.
          schema:
            type: integer
          example: 9500
        X-RateLimit-Reset-Day:
          description: Unix timestamp when the day window resets.
          schema:
            type: integer
          example: 1712016000
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: rate_limited
              message: Rate limit exceeded
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API token or OAuth access token
      description: >
        Send either an API token or an OAuth access token as

        `Authorization: Bearer <token>`. See the Authentication page for
        details.

````