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

# Create or update a ProductLane thread, upserting the contact, in one call.

> Create or update a ProductLane thread, upserting the contact, in one call.

    Thread selection:
      - `thread_id` given      -> operate on that exact thread (reply mode).
      - else contact has an open thread -> patch the most-recently-updated one.
      - else                   -> create a new thread (drops into PL inbox/triage).

    Contact identity: `first_name` + `last_name` fold into the single PL `name`
    (an explicit `contact_name` wins). `website` attaches to the contact's company.

    Reply mode: `reply` posts a message on the thread; with `internal=True` it
    posts a team-only comment instead. `attachments` (public URLs or
    {filename, content_base64} — uploaded to Spaces first) ride along.
    `link` is folded into the body as Markdown.

    Triage: `issue_id` links a Linear-synced issue at create time;
    `issue_ids` / `project_ids` link via customer-needs.



## OpenAPI

````yaml /openapi/private/specs/mcp/sagescreen.yml post /tools/productlane_upsert_thread
openapi: 3.1.0
info:
  title: SageScreen — MCP Tool Catalog
  description: >-
    Agent-callable tools exposed by the SageScreen MCP server at
    `https://mcp.sagescreen.net`.


    These are the same tools served over the Model Context Protocol; this
    reference documents their **REST** surface (`POST /tools/{name}`, body = the
    tool's arguments), which is what the playground below invokes. The live,
    machine-readable catalog is `GET /tools/list`.


    **This page is generated deterministically** from the server's tool registry
    (`apps/docs/scripts/generate_mcp_spec.py`), not hand-written and not
    AI-generated — it cannot drift from the shipped tools.


    All calls authenticate with the `X-Service-Token` header (or `?token=`).
  version: 1.0.0
servers:
  - url: https://mcp.sagescreen.net
    description: Production MCP server
security:
  - serviceToken: []
  - tokenQuery: []
tags:
  - name: Agents
  - name: Contact Push
  - name: Image Generation
  - name: Other
  - name: Productlane
  - name: Reply.io
  - name: Sequences
  - name: Spaces
  - name: WordPress
paths:
  /tools/productlane_upsert_thread:
    post:
      tags:
        - Productlane
      summary: >-
        Create or update a ProductLane thread, upserting the contact, in one
        call.
      description: >-
        Create or update a ProductLane thread, upserting the contact, in one
        call.

            Thread selection:
              - `thread_id` given      -> operate on that exact thread (reply mode).
              - else contact has an open thread -> patch the most-recently-updated one.
              - else                   -> create a new thread (drops into PL inbox/triage).

            Contact identity: `first_name` + `last_name` fold into the single PL `name`
            (an explicit `contact_name` wins). `website` attaches to the contact's company.

            Reply mode: `reply` posts a message on the thread; with `internal=True` it
            posts a team-only comment instead. `attachments` (public URLs or
            {filename, content_base64} — uploaded to Spaces first) ride along.
            `link` is folded into the body as Markdown.

            Triage: `issue_id` links a Linear-synced issue at create time;
            `issue_ids` / `project_ids` link via customer-needs.
      operationId: productlane_upsert_thread
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                contact_email:
                  title: Contact Email
                  type: string
                text:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Text
                contact_name:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Contact Name
                first_name:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: First Name
                last_name:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Last Name
                title:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Title
                website:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Website
                pain_level:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Pain Level
                status:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Status
                project_id:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Project Id
                tag_ids:
                  anyOf:
                    - items:
                        type: string
                      type: array
                    - type: 'null'
                  default: null
                  title: Tag Ids
                is_subscribed:
                  anyOf:
                    - type: boolean
                    - type: 'null'
                  default: null
                  title: Is Subscribed
                company_id:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Company Id
                company_name:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Company Name
                company_external_id:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Company External Id
                thread_id:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Thread Id
                reply:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Reply
                internal:
                  default: false
                  title: Internal
                  type: boolean
                attachments:
                  anyOf:
                    - items: {}
                      type: array
                    - type: 'null'
                  default: null
                  title: Attachments
                link:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Link
                issue_id:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Issue Id
                issue_ids:
                  anyOf:
                    - items:
                        type: string
                      type: array
                    - type: 'null'
                  default: null
                  title: Issue Ids
                project_ids:
                  anyOf:
                    - items:
                        type: string
                      type: array
                    - type: 'null'
                  default: null
                  title: Project Ids
                priority:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Priority
              required:
                - contact_email
              type: object
      responses:
        '200':
          description: >-
            Tool result. The shape is tool-specific; most tools return a JSON
            object, some a `{"result": ...}` envelope.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '400':
          description: Bad arguments (unknown kwargs, malformed JSON body).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Unknown tool name.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Tool raised an error during execution.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message.
        detail:
          type: string
          description: Optional additional context.
      required:
        - error
  responses:
    Unauthorized:
      description: Missing or invalid service token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: Unauthorized
  securitySchemes:
    serviceToken:
      type: apiKey
      in: header
      name: X-Service-Token
    tokenQuery:
      type: apiKey
      in: query
      name: token

````