# Connect Pipedrive

> Source: https://cross-deck.com/docs/connect-pipedrive/ · Crossdeck developer docs (generated from the published page).

Setup
Pipedrive OAuth 2.0 · read-only
·
~7 min read
·
Updated July 17, 2026

Connect your Pipedrive and every sales touch — deals, people, activities (calls, meetings, tasks) — lands on the **same customer timeline** as the website journey Crossdeck already captures. The connector reads Pipedrive on a schedule over **OAuth 2.0** with read-only scopes, joins each record to a person by identity, and surfaces the result in [People](https://cross-deck.com/dashboard/people/index.html) and the customer Journey — plus the **Prism by Crossdeck** card inside Pipedrive. Do one extra thing — stamp the Crossdeck customer id onto a Pipedrive custom field — and the match stops being &ldquo;probably the same person&rdquo; and becomes &ldquo;*provably* the same person.&rdquo; Crossdeck only ever reads. Nothing is written back to your Pipedrive.

## TL;DR

- **Connect read-only in one click.** Dashboard &rarr; Developers &rarr; Integrations &rarr; Pipedrive &rarr; **Connect**. OAuth 2.0, read-only scopes (deals, contacts, activities, mail). You approve in Pipedrive's own UI; no API token to paste.

- **Sales touches join the website journey.** Deals, people, and activities land on the *same* customer timeline as page views, sign-ups, and revenue — joined by identity, not stitched by hand.

- **Stamp the Crossdeck ID for an exact match.** Write the Crossdeck customer id into a Pipedrive Person custom field (e.g. &ldquo;Crossdeck ID&rdquo;), then give Crossdeck that field's API key. That owned id is the strong deterministic join.

- **Email is the fallback floor, not the ceiling.** Without the stamped id, Crossdeck matches on email — softer, but never nothing. Both resolve to the *same* customer, never a duplicate.

- **Events you'll see:** `deal_stage_changed`, `lead_created`, `activity_created`, `activity_completed`, `email_sent` / `email_received` — each carrying the person's company when set, on the timeline and on the Prism card.

- **Read-only, always.** Crossdeck never writes to Pipedrive. Tokens live in Google Cloud Secret Manager. Disconnect anytime from the dashboard.

## What you get

Pipedrive is where your sales team's version of the customer lives — the deals, the calls, the meetings, the leads. Crossdeck already holds the *product* version: the anonymous first visit, the sign-up, the feature usage, the subscription, the errors they hit. On their own, each is half the picture. Connecting Pipedrive merges the two halves onto one timeline, keyed to one person.

Concretely, once connected:

- **Deals** and their stage changes appear on the customer's Journey — you can see the marketing-attributed visit that started six weeks before the deal was opened, and the product usage that ran alongside the sales cycle.

- **People** (Pipedrive Persons) resolve to the Crossdeck customer they already are — not a second, duplicate record. One human, whether they came in through the website or the sales team typed them into Pipedrive.

- **Activities** — calls, meetings, tasks — land as timeline events when they're created and when they're completed, so &ldquo;we called them Tuesday&rdquo; sits next to &ldquo;they logged in Wednesday.&rdquo;

- **The Prism by Crossdeck card** renders *inside* Pipedrive, so your reps see the product-side truth (last active, plan, entitlement, recent errors) without leaving the CRM. This ships with the integration, the same as every Crossdeck integration.

Read-only, by design.

Crossdeck reads your Pipedrive; it does not write to it. No deals are created, no fields are overwritten, no contacts are mutated. The one write in this whole integration is one *you* make — stamping the Crossdeck ID onto a Person field from *your* backend ([Step 2](#stamp-id)). That's your data, written by your code.

## How the loop turns on — the event is the key

The integration has two halves, and it's worth being precise about which is which, because only one of them is automatic.

**The half Crossdeck runs (automatic once you connect):** the OAuth connection is **read-only**. Crossdeck *reads* the Persons, Deals and Activities that are *already in* your Pipedrive, joins them to the person's journey, and renders the card. Connecting does not create anything in Pipedrive — it can only read what's there.

**The half you run (the &ldquo;on switch&rdquo;):** records only reach Pipedrive because *your* backend puts them there, and that is triggered by **an event you fire** — the moment identity becomes real for you. That event is whatever fits your product: an **enquiry-form submission**, a **sign-up**, a **login**, a **purchase**. In that one event handler, your backend does two things: identifies the person in Crossdeck to get their `cdcust_…`, and creates or updates the Pipedrive Person (and Lead/Deal) with that `cdcust_…` stamped on the Crossdeck ID field.

That handler is the key that turns the engine on. Without it, nothing lands in Pipedrive for Crossdeck to read, so nothing appears on the card. With it, the whole loop runs on its own:

```
An event you fire   (enquiry submitted · sign-up · login · purchase)
  &rarr; your backend identifies the person in Crossdeck &rarr; gets their cdcust_…
  &rarr; your backend creates/updates the Pipedrive Person (+ Lead/Deal),
     stamping cdcust_… on the "Crossdeck ID" field
  &rarr; Crossdeck's read-only connector picks it up (Step 1 & 3)
  &rarr; the sales record joins the person's website & product journey
  &rarr; the Prism card shows the whole story
```

Connecting alone is not enough — you fire the event.

OAuth wires Crossdeck to read your Pipedrive; it never reaches back to create a lead when someone acts on your site. The line that creates the Pipedrive record (and stamps the id) lives in *your* event handler — Steps 2 and 3 below are exactly that handler. The Crossdeck SDK on your site (capturing the journey and giving you the `cdcust_…`) is the front of this; the Pipedrive write is the back of it.

## Step 1 — Connect Pipedrive

In the Crossdeck dashboard, open **Developers &rarr; Integrations &rarr; Pipedrive** and click **Connect**. Crossdeck redirects you to Pipedrive's OAuth 2.0 authorize screen, where you approve the connection *in Pipedrive's own UI* under whichever company account you're signed into. There is no API token to generate and paste.

Crossdeck requests **read-only** scopes only:

Scope
What it reads

deals:read
Deals and their stage changes — the pipeline movement that becomes deal_stage_changed and lead_created .

contacts:read
Persons and organizations — the identity anchors (email, and the stamped Crossdeck ID custom field) the connector joins on.

activities:read
Activities — calls, meetings, tasks — and their completion state, which become activity_created and activity_completed .

mail:read
Read scope reserved for mail-linked context. Mail-open events are not emitted yet — see What flows into the journey .

Approve, and Pipedrive hands Crossdeck an access token and refresh token. Crossdeck stores them in Google Cloud Secret Manager and the connector begins polling Pipedrive on a schedule — pulling deals, persons, and activities, and reconciling them onto the customer timeline. The Pipedrive card flips to **Connected**.

You can connect first and map the field later.

Connecting is enough to start seeing sales touches on the timeline — matched on email. The exact-match handshake ([Step 2](#stamp-id) and [Step 3](#map-field)) is the upgrade from &ldquo;probably&rdquo; to &ldquo;provably.&rdquo; Do it when you're ready; nothing breaks in the meantime.

## Step 2 — Stamp the Crossdeck ID onto a Pipedrive custom field

This is the handshake that makes the match exact. Email works as a fallback ([Identity model](#identity)), but email is soft — it changes, it's shared, it's mistyped, and Sign in with Apple's Private Relay hands you a relay address that won't match. An id *you own* on both sides is hard. So you carry the Crossdeck customer id into Pipedrive and put it on the Person.

### Create (or choose) the custom field

In Pipedrive, go to **Settings &rarr; Data fields &rarr; Person** and add a custom field — a single-line **text** field named, say, **Crossdeck ID**. Pipedrive assigns it a 40-character hex **field API key** (e.g. `a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2`). You'll need that key in [Step 3](#map-field). Copy it now from the field's settings.

### Get the Crossdeck id

The value you stamp is the Crossdeck customer id — the canonical `cdcust_…` handle for the person. Where you read it from depends on where the identity is anchored:

- **You call `identify()` in your app.** The SDK resolves the identity graph and persists the Crossdeck customer id locally. Read it with `Crossdeck.diagnostics().crossdeckCustomerId` (client) after `identify()` has resolved. This is the same `cdcust_…` the whole platform keys on. See [Identify users — the alias model](https://cross-deck.com/docs/identify-users/index.html#alias-model).

- **You identify server-side** (e.g. in a checkout or webhook handler). The Node SDK's `identify()` returns the resolved customer id; use that as your checkout reference and carry it forward. See [server-side identify](https://cross-deck.com/docs/identify-users/index.html#server-side).

- **No login yet.** On a public site your identity anchor is the email the visitor typed — `identify(email, { email })` — and the graph collapses that to a `cdcust_…`. Either stamp that resolved id, or stamp the email and let Crossdeck's email fallback do the join until a durable id exists.

### Authenticate the write with your own Pipedrive credentials

This write goes to *your* Pipedrive, so it uses *your own* Pipedrive credentials — separate from the read-only connection Crossdeck made in [Step 1](#connect). Crossdeck's token is read-only and Crossdeck's; it can't and won't write on your behalf. Your backend authenticates itself to Pipedrive, two ways:

- **A personal API token — simplest to start.** In Pipedrive, go to **Settings &rarr; Personal preferences &rarr; API** and copy your token. Keep it server-side (an env var), and pass it as the `api_token` query parameter or a Bearer header. It's tied to one user — ideal for a first-party backend writing to your own account. [How to find your API token &rarr;](https://pipedrive.readme.io/docs/how-to-find-the-api-token)

- **OAuth 2.0 — for production or multiple accounts.** Register your own Pipedrive app and use its access token (refresh it before it expires). [Pipedrive OAuth authorization &rarr;](https://pipedrive.readme.io/docs/marketplace-oauth-authorization)

The call itself is Pipedrive's **Persons** endpoint — `POST` to create, `PATCH` to update — against your company domain, e.g. `https://{yourcompany}.pipedrive.com/api/v2/persons` (or `https://api.pipedrive.com/v1/persons`). You set the Crossdeck ID by its 40-character field key. Pipedrive's own references, so you don't have to hunt for them:

- [Persons API — add & update a person &rarr;](https://developers.pipedrive.com/docs/api/v1/Persons)

- [Custom fields — how the 40-character keys work &rarr;](https://pipedrive.readme.io/docs/core-api-concepts-custom-fields)

- [Setting a custom-field value via the API &rarr;](https://developers.pipedrive.com/tutorials/update-custom-field-pipedrive-api)

**Mind the host &harr; path pairing — this is the one that trips people up.** Pipedrive has two API hosts with *different* path prefixes, and you can't mix them: on `api.pipedrive.com` the path is `/v1/…` (e.g. `/v1/persons`, `/v1/leads`) — **not** `/api/v1/…`; on your *company* domain it's `/api/v1/…` or `/api/v2/…` (e.g. `https://{yourcompany}.pipedrive.com/api/v1/leads`). Calling `api.pipedrive.com/api/v1/leads` returns `404 Unknown method`. So if your flow also creates a **Lead**, it is `POST https://api.pipedrive.com/v1/leads` — [Leads API &rarr;](https://developers.pipedrive.com/docs/api/v1/Leads).

### Write it when your backend touches the Person

Whenever your backend creates or updates a Pipedrive Person — the moment a lead is captured, a trial converts, or a deal is opened — set the custom field to the Crossdeck id. This is *your* write, from *your* code, against *your* Pipedrive token; Crossdeck is not involved in it.

```
// your backend — upsert a Pipedrive Person with the Crossdeck ID stamped.
// CROSSDECK_ID_FIELD is the 40-char field API key from Step 2.
const CROSSDECK_ID_FIELD = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"

async function upsertPipedrivePerson({ email, name, crossdeckId }) {
  const res = await fetch("https://api.pipedrive.com/v1/persons", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${PIPEDRIVE_ACCESS_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name,
      email: [{ value: email, primary: true }],
      // the custom field is keyed by its API key, value = the cdcust_… id
      [CROSSDECK_ID_FIELD]: crossdeckId,
    }),
  })
  return res.json()
}

// crossdeckId comes from your identify() call — the resolved cdcust_… handle.
await upsertPipedrivePerson({
  email: "dana@acme.com",
  name: "Dana Okafor",
  crossdeckId: "cdcust_9f3a7c21b0e4",
})
```

Custom fields are addressed by API key, not label.

In Pipedrive's REST API a custom field is set by its 40-character key, not its display name. That's why you copy the key in Step 2 — both your write here and Crossdeck's read in Step 3 refer to the same key. Renaming the field's label later changes nothing; the key is stable.

### Persons typed in by hand

Not every Person is created by your backend — a rep will often just type someone into Pipedrive. That case is covered, and you don't have to do anything special:

- **Do nothing and it still works — by email.** A hand-typed Person with an email Crossdeck already knows resolves on the email fallback automatically. The card shows; the join is just soft rather than provable.

- **Want the exact match on a manual Person? Paste the id.** Open the person in Crossdeck, copy their `cdcust_…` from the customer page, and paste it into the **Crossdeck ID** field on the Pipedrive Person. Same field, same effect as the backend write — it upgrades that person from email-matched to provably-matched.

So the rule of thumb: automate the stamp from your backend where you can (it scales); for one-off or manually-entered records, email carries them, and a hand-pasted id makes any single one exact.

## Step 3 — Map the field in Crossdeck

Now tell Crossdeck which field holds the id. In the Pipedrive integration settings in the dashboard, paste the **custom field API key** from Step 2 into **Crossdeck ID field** and save.

From that point, when the connector reads a Pipedrive Person, it looks at that field first. If it finds a Crossdeck id there, it resolves the *exact* person by that owned id — a deterministic join, no guessing. If the field is empty (or you haven't mapped it), the connector falls back to matching on the Person's email address.

What Crossdeck sees on the Person How it resolves Strength

The mapped field holds a Crossdeck id
Exact match on your owned id — deterministic.
Provably the same person

Field empty / unmapped, email present
Match on email against the identity graph.
Probably the same person

No id, no matchable email
Held as a Pipedrive-only record until an anchor appears.
Unresolved (not a duplicate customer)

That top row is the whole point of Steps 2 and 3. The difference between the first two rows is the difference between a report you can stake a renewal forecast on and one you have to caveat.

## What flows into the journey

The connector reads Pipedrive on a schedule and emits these events onto the customer timeline, joined by identity. You'll see them in [People](https://cross-deck.com/dashboard/people/index.html), on the customer Journey, and on the Prism card:

Event Fires when

lead_created A new person, deal, or Pipedrive Lead is opened.
deal_stage_changed A deal moves between pipeline stages (including won / lost).
activity_created An activity — call, meeting, task — is scheduled or logged.
activity_completed An activity is marked done.
email_sent / email_received A mailbox email to/from the person (correspondence — not opens; see below).

Each event carries its Pipedrive context (deal id, stage, activity type, and the person's **organization / company** when set) and attaches to the resolved customer — so a stage change sits on the same timeline as that person's page views, sign-up, and subscription. That cross-layer join is the thing no CRM-only or product-only tool can show you.

What's honest about the edges.

**Email correspondence** (sent / received) flows, but recipient **open and click tracking is not exposed to apps by Pipedrive** (it's their premium email feature), so Crossdeck surfaces messages, never opens. The **Pipedrive Lead object** emits `lead_created` once Pipedrive ships its OAuth scope for Leads (pending on their side) — until then, new deals already carry the lead signal, so nothing's missing. **Webhooks** (push instead of the scheduled poll) aren't available yet either. Don't build on opens or webhooks until they ship.

## Identity model — one person, every key

Crossdeck's canonical identity is the `cdcust`. Its identity graph collapses every alias you hand it — the anonymous device, an email, and any durable rail or CRM id — into **one person**. Pipedrive is just another source feeding that graph, and it feeds it two keys with two strengths:

- **The stamped Crossdeck ID — the strong deterministic join.** Because it's an id you own on both sides, a match on it is exact. This is the join you want, and it's why Steps 2 and 3 exist.

- **Email — the fallback floor.** When the id isn't there, email still joins the Pipedrive Person to the same graph entry the website journey built. Softer — but a floor, never a gap.

**Both resolve to the same customer — never a duplicate.** The exact id and the email fallback are two roads to the *same* `cdcust_…`. Adding the id later doesn't fork the person into two; it upgrades an email-matched person to a provably-matched one, in place. That's the guarantee: a sales touch and a product session for one human always land on one timeline.

Provably vs probably — and why it's your call.

Crossdeck won't invent a strong id it wasn't given. If you stamp the Crossdeck ID, you get deterministic matching; if you don't, you get email matching — honest about which it is, never dressing a soft match up as a hard one. The owned id is the difference, and putting it there is the one action that's yours to take.

## Read-only & security

- **Read-only.** Crossdeck holds read scopes only and has no write path into your Pipedrive. It cannot create, edit, or delete deals, persons, or activities. The Crossdeck ID field is written by *your* backend, not by Crossdeck.

- **Tokens in Secret Manager.** The OAuth access and refresh tokens Pipedrive issues are stored in Google Cloud Secret Manager — never in Firestore, never in the browser, never in a bundle. The connector reads them server-side to poll the API.

- **Disconnect anytime.** Remove the connection from **Developers &rarr; Integrations &rarr; Pipedrive** in the dashboard. Crossdeck stops polling and discards the tokens. Data already on the timeline stays; no new Pipedrive events ingest until you reconnect.

## Troubleshooting

### Connected, but sales touches aren't appearing on the timeline

The connector polls on a schedule, so allow a few minutes after connecting for the first pull. If touches still don't appear, the people aren't resolving to a customer — check that the Pipedrive Persons have either a mapped Crossdeck ID ([Step 3](#map-field)) or an email that matches a known customer. A Person with neither is held as a Pipedrive-only record, not merged.

### Deals show up under the wrong person, or as new people

Almost always the exact-match handshake isn't in place. Without the stamped id, the connector matches on email — so a Person whose Pipedrive email differs from the email Crossdeck knows (a work vs personal address, a relay address) resolves softly or not at all. Stamp the Crossdeck ID ([Step 2](#stamp-id)) and map the field ([Step 3](#map-field)) to make the match deterministic.

### The Crossdeck ID field is set in Pipedrive but Crossdeck still matches on email

The field API key in the dashboard doesn't match the field you're writing to. Pipedrive custom fields are addressed by their 40-character key, not their label — re-copy the key from **Settings &rarr; Data fields &rarr; Person** and confirm it's the exact string both your backend write and the dashboard mapping use.

### I expected a mail-open event and don't see one

Mail opens aren't emitted yet. The connector surfaces the four events in [What flows into the journey](#flows) only; the `mail:read` scope is reserved for future context. Nothing is wrong with your setup.

## What's next

- [Identify users](https://cross-deck.com/docs/identify-users/index.html) — where the Crossdeck customer id comes from, the alias model, and how to read it client- and server-side to stamp onto Pipedrive.

- [How identity works](https://cross-deck.com/docs/how-identity-works/index.html) — the identity graph and how a CRM id, an email, and an anonymous device collapse to one person.

- [Identity verification](https://cross-deck.com/docs/identity-verification/index.html) — how Crossdeck resolves and de-duplicates people across sources.

- [Prism](https://cross-deck.com/docs/prism/index.html) — the card that renders inside Pipedrive, and reading the joined portfolio from your own AI.

- [Node SDK](https://cross-deck.com/docs/node-sdk/index.html) — the server-side `identify()` you use to resolve the id before stamping it.

Read-only Pipedrive connector — OAuth 2.0 connect, scheduled poll for deals / persons / activities / organizations / email correspondence, exact-match on a stamped Crossdeck ID custom field with email fallback, and the Prism card endpoint (July 17, 2026). Email opens, the Lead-object scope, and webhooks are pending on Pipedrive's side. Related: [Identify users](https://cross-deck.com/docs/identify-users/index.html), [How identity works](https://cross-deck.com/docs/how-identity-works/index.html).
