> ## 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.

# Push a contact to Pipedrive using structured person / org / deal objects.

> Push a contact to Pipedrive using structured person / org / deal objects.

    Returns {job_id, status:'accepted'} immediately; poll contact_push_status.

    Required identification:
      user_id:  Pipedrive user id who will OWN the created person/org/deal.
      person:   Required dict. Must contain `li_url` and/or `email`. The person
                object also holds all person-level fields (built-in or custom),
                addressed by Pipedrive field KEY (see "Field keys" below), plus
                its own `overwrite` flag.

                  person = {
                    "li_url":   "https://linkedin.com/in/...",   # well-known: dedupe key
                    "email":    "theo@x.com",                    # well-known: dedupe key
                    "name":     "Theo Meinzer",                  # well-known: caller name always wins
                    "phone":    "+1...",                         # any PD field key
                    "<hash>":   "...",                           # any custom field key
                    "overwrite": false
                  }

    Org (optional):
      org:      Dict. Identification keys drive create-or-match (Pipedrive is
                SOT for dedupe). Other keys are PD field-key writes.
                Omit `org` to skip org entirely (person will not be linked).

                  org = {
                    "li_url": "https://linkedin.com/company/...", # well-known: tried first
                    "name":   "SBH Fashion",                      # well-known: name match / create
                    "<any field key>": "value",
                    "overwrite": false
                  }

                Org lookup order: LI URL → name exact. On miss + `name` provided,
                a new org is created (and linked to `li_url` if given so future
                lookups dedupe).

    Deal (optional):
      deal:     Dict. Omit `deal` entirely to skip deal creation/reuse.
                If provided (even as `{}`), an OPEN deal on this person in
                the chosen pipeline is reused; otherwise a new one is created.

                  deal = {
                    "stage":       "new",   # well-known: stage NAME for new deals
                    "pipeline_id": 5,       # well-known: defaults to CONTACT_PUSH_DEFAULT_PIPELINE_ID
                    "title":       "...",   # well-known: defaults to "<Name> - <Company>"
                    "value":       50000,   # any PD field key
                    "currency":    "USD",
                    "<hash>":      "...",
                    "overwrite":   false
                  }

    Top-level behavior flags (all default off / unset):
      sequence_id:      Reply.io campaign id; enroll the email there (silent no-op if no email).
      dm:               LinkedIn DM text. If non-empty AND skip_dm_activity is false,
                        creates a "linkedin_message" activity marked done=today.
      notes:            Free-text appended under "--- Notes ---" in the Pipedrive note.
      skip_note:        Skip the ENTIRE note. Overrides skip_blob_note; `notes` dropped.
      skip_blob_note:   Suppress only the auto LinkedIn-header + blob portion;
                        `notes` still creates a note if non-empty.
      skip_dm_activity: Skip the DM activity even when `dm` is supplied.
      skip_followup:    Skip the 5-day followup task on newly-created deals.
      skip_enrich:      Skip Apollo entirely — no enrichment, no LI-URL pre-fetch
                        from email. Useful when records are already enriched
                        upstream. Caller-supplied `person.name`, `org.name`,
                        `org.li_url` are the only sources in this mode.

    Dedupe behavior:
      Person lookup order: (1) LinkedIn URL custom field → (2) email exact match
                   → (3) name exact match. First hit wins; existing person is
                   reused and any missing fields backfilled from Apollo (unless
                   `skip_enrich=true`).
      Org lookup: by LI URL (if `org.li_url`) → name exact. Reused, never duplicated.
      Deal lookup: an OPEN deal on this person in this pipeline is reused.

    Field keys:
      Inside `person`, `org`, `deal`, any key NOT in the well-known set is
      treated as a Pipedrive field KEY — either a 40-char hashed custom-field
      key (e.g. "855c1c9c8864dad6fa56c82fd957b9c1a214508b") or a short stable
      key for built-ins (e.g. "phone", "value", "currency"). Field NAMES are
      not supported. Use `pd_fields_list` to discover keys. Unknown keys raise
      a ValueError listing the offending {entity, field}.

    Overwrite:
      Each object carries its own `overwrite` flag. When false (default), only
      empty fields on the existing record are filled (backfill). When true,
      every provided field is written, replacing whatever was there.



## OpenAPI

````yaml /openapi/private/specs/mcp/sagescreen.yml post /tools/contact_push
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/contact_push:
    post:
      tags:
        - Contact Push
      summary: >-
        Push a contact to Pipedrive using structured person / org / deal
        objects.
      description: >-
        Push a contact to Pipedrive using structured person / org / deal
        objects.

            Returns {job_id, status:'accepted'} immediately; poll contact_push_status.

            Required identification:
              user_id:  Pipedrive user id who will OWN the created person/org/deal.
              person:   Required dict. Must contain `li_url` and/or `email`. The person
                        object also holds all person-level fields (built-in or custom),
                        addressed by Pipedrive field KEY (see "Field keys" below), plus
                        its own `overwrite` flag.

                          person = {
                            "li_url":   "https://linkedin.com/in/...",   # well-known: dedupe key
                            "email":    "theo@x.com",                    # well-known: dedupe key
                            "name":     "Theo Meinzer",                  # well-known: caller name always wins
                            "phone":    "+1...",                         # any PD field key
                            "<hash>":   "...",                           # any custom field key
                            "overwrite": false
                          }

            Org (optional):
              org:      Dict. Identification keys drive create-or-match (Pipedrive is
                        SOT for dedupe). Other keys are PD field-key writes.
                        Omit `org` to skip org entirely (person will not be linked).

                          org = {
                            "li_url": "https://linkedin.com/company/...", # well-known: tried first
                            "name":   "SBH Fashion",                      # well-known: name match / create
                            "<any field key>": "value",
                            "overwrite": false
                          }

                        Org lookup order: LI URL → name exact. On miss + `name` provided,
                        a new org is created (and linked to `li_url` if given so future
                        lookups dedupe).

            Deal (optional):
              deal:     Dict. Omit `deal` entirely to skip deal creation/reuse.
                        If provided (even as `{}`), an OPEN deal on this person in
                        the chosen pipeline is reused; otherwise a new one is created.

                          deal = {
                            "stage":       "new",   # well-known: stage NAME for new deals
                            "pipeline_id": 5,       # well-known: defaults to CONTACT_PUSH_DEFAULT_PIPELINE_ID
                            "title":       "...",   # well-known: defaults to "<Name> - <Company>"
                            "value":       50000,   # any PD field key
                            "currency":    "USD",
                            "<hash>":      "...",
                            "overwrite":   false
                          }

            Top-level behavior flags (all default off / unset):
              sequence_id:      Reply.io campaign id; enroll the email there (silent no-op if no email).
              dm:               LinkedIn DM text. If non-empty AND skip_dm_activity is false,
                                creates a "linkedin_message" activity marked done=today.
              notes:            Free-text appended under "--- Notes ---" in the Pipedrive note.
              skip_note:        Skip the ENTIRE note. Overrides skip_blob_note; `notes` dropped.
              skip_blob_note:   Suppress only the auto LinkedIn-header + blob portion;
                                `notes` still creates a note if non-empty.
              skip_dm_activity: Skip the DM activity even when `dm` is supplied.
              skip_followup:    Skip the 5-day followup task on newly-created deals.
              skip_enrich:      Skip Apollo entirely — no enrichment, no LI-URL pre-fetch
                                from email. Useful when records are already enriched
                                upstream. Caller-supplied `person.name`, `org.name`,
                                `org.li_url` are the only sources in this mode.

            Dedupe behavior:
              Person lookup order: (1) LinkedIn URL custom field → (2) email exact match
                           → (3) name exact match. First hit wins; existing person is
                           reused and any missing fields backfilled from Apollo (unless
                           `skip_enrich=true`).
              Org lookup: by LI URL (if `org.li_url`) → name exact. Reused, never duplicated.
              Deal lookup: an OPEN deal on this person in this pipeline is reused.

            Field keys:
              Inside `person`, `org`, `deal`, any key NOT in the well-known set is
              treated as a Pipedrive field KEY — either a 40-char hashed custom-field
              key (e.g. "855c1c9c8864dad6fa56c82fd957b9c1a214508b") or a short stable
              key for built-ins (e.g. "phone", "value", "currency"). Field NAMES are
              not supported. Use `pd_fields_list` to discover keys. Unknown keys raise
              a ValueError listing the offending {entity, field}.

            Overwrite:
              Each object carries its own `overwrite` flag. When false (default), only
              empty fields on the existing record are filled (backfill). When true,
              every provided field is written, replacing whatever was there.
      operationId: contact_push
      requestBody:
        required: true
        content:
          application/json:
            schema:
              properties:
                user_id:
                  title: User Id
                  type: integer
                person:
                  additionalProperties: true
                  title: Person
                  type: object
                org:
                  anyOf:
                    - type: object
                      additionalProperties: true
                    - type: 'null'
                  default: null
                  title: Org
                deal:
                  anyOf:
                    - type: object
                      additionalProperties: true
                    - type: 'null'
                  default: null
                  title: Deal
                dm:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Dm
                notes:
                  anyOf:
                    - type: string
                    - type: 'null'
                  default: null
                  title: Notes
                sequence_id:
                  anyOf:
                    - type: integer
                    - type: 'null'
                  default: null
                  title: Sequence Id
                skip_note:
                  anyOf:
                    - type: boolean
                    - type: 'null'
                  default: false
                  title: Skip Note
                skip_blob_note:
                  anyOf:
                    - type: boolean
                    - type: 'null'
                  default: false
                  title: Skip Blob Note
                skip_dm_activity:
                  anyOf:
                    - type: boolean
                    - type: 'null'
                  default: false
                  title: Skip Dm Activity
                skip_followup:
                  anyOf:
                    - type: boolean
                    - type: 'null'
                  default: false
                  title: Skip Followup
                skip_enrich:
                  anyOf:
                    - type: boolean
                    - type: 'null'
                  default: false
                  title: Skip Enrich
              required:
                - user_id
                - person
              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

````