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

# Update Project

> Update a project while in `draft` or `awaiting_test`. `organization_id` / `brand_id` / `campaign_id` / `channel` / `toll_free_verification_id` are immutable after creation; including them in the body returns a 400. All other fields are partial: send only the keys you want to change.



## OpenAPI

````yaml /api-reference/openapi.json patch /projects/{id}
openapi: 3.1.0
info:
  title: Political Comms API
  summary: >-
    Direct-to-carrier political texting API for campaigns, PACs, advocacy
    organizations, fundraisers, and elected officials.
  description: >-
    Public REST API for the Political Comms platform. Surfaces include Projects
    (compose, test, schedule, send), Contact Lists (S3 import and analysis),
    Media Files, Organizations and hierarchy, Brands, Campaigns, Tracking
    Domains, Phone Numbers, Analytics, and Billing.


    Authentication is an API key passed in the `X-API-Key` header. Keys are
    generated from the dashboard at Admin → API Keys and are prefixed
    `pc_live_`. All POST and PATCH endpoints that mutate state are designed to
    be safe to retry, with optional `Idempotency-Key` headers for stronger
    guarantees. Rate limit is 100 requests per hour per key.


    Webhooks emit `message.sent`, `message.delivered`, `message.failed`,
    `message.replied`, and `link.clicked` events. Payloads are HMAC-signed;
    validate the signature before trusting any payload.


    A Model Context Protocol (MCP) server is available at
    https://docs.politicalcomms.com/mcp for AI agents that need to search the
    documentation programmatically. The developer hub at
    https://politicalcomms.com/developers/ has quickstart examples in cURL, raw
    HTTP, and Python.
  version: 1.0.0
  termsOfService: https://politicalcomms.com/terms/
  contact:
    name: Political Comms Support
    email: support@politicalcomms.com
    url: https://docs.politicalcomms.com
  license:
    name: Proprietary
    url: https://politicalcomms.com/terms/
  x-logo:
    url: https://politicalcomms.com/images/brand/pcomms-logo-left-of-text.png
    altText: Political Comms
    backgroundColor: '#ffffff'
    href: https://politicalcomms.com/
  x-mcp:
    url: https://docs.politicalcomms.com/mcp
    discovery_url: https://docs.politicalcomms.com/.well-known/mcp
    transport: http
    auth: none
    tools:
      - search_political_comms
      - query_docs_filesystem_political_comms
servers:
  - url: https://api.politicalcomms.com/v1
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: Organizations
    description: List descendant organizations and hierarchy.
  - name: Brands
    description: List brands across your organization hierarchy.
  - name: Campaigns
    description: List campaigns across your organization hierarchy.
  - name: Tracking Domains
    description: >-
      List active link-tracking domains. Use the returned ids as
      `link_tracking_domain_id` on project create/update.
  - name: Phone Numbers
    description: List phone numbers across 10DLC campaigns and toll-free verifications.
  - name: Toll-Free Verifications
    description: >-
      List toll-free verifications (carrier registrations) across your
      organization hierarchy.
  - name: Contact Lists
    description: List, import, and analyze contact lists.
  - name: Media Files
    description: List, import, and fetch media files.
  - name: Projects
    description: Create, edit, test, schedule, and inspect projects.
  - name: Analytics
    description: Message statistics and delivery performance.
  - name: Billing
    description: Usage and billing data across your organization.
paths:
  /projects/{id}:
    patch:
      tags:
        - Projects
      summary: Update Project
      description: >-
        Update a project while in `draft` or `awaiting_test`. `organization_id`
        / `brand_id` / `campaign_id` / `channel` / `toll_free_verification_id`
        are immutable after creation; including them in the body returns a 400.
        All other fields are partial: send only the keys you want to change.
      operationId: updateProject
      parameters:
        - $ref: '#/components/parameters/IdPath'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                message_text:
                  type: string
                protocol:
                  type: string
                  enum:
                    - sms
                    - mms
                phone_number_ids:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  maxItems: 49
                  description: >-
                    Replace the project's sending phone numbers (1-49). New
                    conversations rotate across them; existing conversations
                    keep their already-assigned sticky number.
                phone_number_id:
                  type: string
                  deprecated: true
                  description: >-
                    Deprecated — use phone_number_ids. A single id is treated as
                    a one-element phone_number_ids.
                contact_list_ids:
                  type: array
                  items:
                    type: string
                  minItems: 1
                suppression_list_ids:
                  type: array
                  items:
                    type: string
                media_ids:
                  type: array
                  items:
                    type: string
                link_tracking_enabled:
                  type: boolean
                link_tracking_destination_url:
                  type:
                    - string
                    - 'null'
                  format: uri
                link_tracking_domain_id:
                  type:
                    - string
                    - 'null'
                link_tracking_param_field:
                  type:
                    - string
                    - 'null'
                  maxLength: 64
                  description: >-
                    Contact field appended as a redirect query param on tracking
                    links ('phone', a custom-field name, or null for none).
            example:
              name: Spring Outreach (revised)
              message_text: 'Hi {first_name}, early voting starts Monday. Learn more: {link}'
              media_ids: []
      responses:
        '200':
          description: Project updated
          content:
            application/json:
              example:
                success: true
                data:
                  id: proj_abc123
                  name: Spring Outreach (revised)
                  status: draft
                  message_text: >-
                    Hi {first_name}, early voting starts Monday. Learn more:
                    {link}
                  updated_at: '2026-03-21T09:30:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    IdPath:
      name: id
      in: path
      required: true
      description: Resource ID
      schema:
        type: string
  responses:
    BadRequest:
      description: Invalid parameters or malformed request
      content:
        application/json:
          example:
            success: false
            error: Invalid request parameters
            code: BAD_REQUEST
            statusCode: 400
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          example:
            success: false
            error: The provided API key is invalid or has been revoked
            code: INVALID_API_KEY
            statusCode: 401
    NotFound:
      description: Resource does not exist
      content:
        application/json:
          example:
            success: false
            error: Resource not found
            code: NOT_FOUND
            statusCode: 404
    RateLimited:
      description: Rate limit exceeded
      headers:
        X-RateLimit-Limit:
          description: Maximum requests allowed in the current window
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: Requests remaining in the current window
          schema:
            type: integer
        X-RateLimit-Reset:
          description: Unix timestamp (seconds) when the limit resets
          schema:
            type: integer
      content:
        application/json:
          example:
            success: false
            error: >-
              Rate limit exceeded. Try again after the X-RateLimit-Reset
              timestamp.
            code: RATE_LIMIT_EXCEEDED
            statusCode: 429
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        Authenticate every request by passing your API key in the X-API-Key
        header. Keys are scoped to your organization hierarchy.

````