Packages
Elixir client for the Pipedrive CRM API. v2-first (Deals, Persons, Orgs, Activities, Pipelines, Stages, Products, Search, Fields), with OAuth TokenStore, retries/telemetry, Raw escape hatch, and webhook Event normalization. Inbound Plug helpers live in `ex_pipedrive_web`. Forked from LineDrive.
Current section
Files
Jump to
Current section
Files
ex_pipedrive
CHANGELOG.md
CHANGELOG.md
# Changelog
All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.2.0] - 2026-09-02
Coverage and packaging since the first Hex release. **Breaking for inbound
webhooks:** add [`ex_pipedrive_web`](packages/ex_pipedrive_web) if you used
`ExPipedrive.Incoming.Handler` from core.
### Added
- Run Dialyzer in CI on the primary Elixir 1.17 matrix cell with PLT caching;
document `mix dialyzer` in the local quality gate
([#84](https://github.com/blksheep80/ex_pipedrive/issues/84)).
- Optional sibling package `ex_pipedrive_phoenix` (`packages/ex_pipedrive_phoenix`)
for marketplace OAuth install helpers (independent of Überauth)
([#21](https://github.com/blksheep80/ex_pipedrive/issues/21)).
- Optional sibling package `ex_pipedrive_oban` (`packages/ex_pipedrive_oban`)
with cursor-aware Oban sync workers and rate-limit snooze
([#20](https://github.com/blksheep80/ex_pipedrive/issues/20)).
- Optional sibling package `ex_pipedrive_web` (`packages/ex_pipedrive_web`)
with inbound webhook Plug helpers; Event structs stay in core
([#82](https://github.com/blksheep80/ex_pipedrive/issues/82)).
- Expand `ExPipedrive.Webhook.Event` beyond deal/person: typed decode for
organization, activity, lead, note, product, pipeline, stage, user,
activityType, deal_product, deal_installment, project, task, and board;
v1 `merged` plus v2 `create`/`change`/`delete`; real v2 payloads synthesize
an event name from `meta.action`/`meta.entity`; unknown resources stay maps
([#81](https://github.com/blksheep80/ex_pipedrive/issues/81)).
### Fixed
- `ExPipedrive.Users.find_users_by_name/3` matched atom-key response bodies
(`%{success: true, data: data}`) and silently returned no matches against
Jason-decoded (string-key) JSON. Now matches `%{"data" => data}` like every
other resource module ([#67](https://github.com/blksheep80/ex_pipedrive/issues/67)).
- Preserve API-provided lead value currencies and leave bare numeric values
currency-less.
### Changed
- **Inbound webhook Plug** — `ExPipedrive.Incoming.Handler` moved to sibling
package `ex_pipedrive_web` as `ExPipedriveWeb.Incoming.Handler` (old module
names remain compatibility aliases there). Core no longer depends on Plug
([#82](https://github.com/blksheep80/ex_pipedrive/issues/82)).
- **Facade / twin cleanup** — Document blessed path (`ExPipedrive.Deals.get/2`,
`list_page/2`, `stream/2`, …). Soft-deprecate legacy v1 twin names on resource
modules and the root facade; nothing removed yet
([#79](https://github.com/blksheep80/ex_pipedrive/issues/79)).
- **Resource adoption** — `Deals`, `Persons`, `Organizations`, `Activities`,
and `Pipelines` implement `ExPipedrive.Resource` for v2 CRUD/list/stream
(same pattern as Products/Stages). Public APIs unchanged; v1 legacy helpers
retained ([#78](https://github.com/blksheep80/ex_pipedrive/issues/78)).
- **List return shapes** — `Notes.get_all_org_notes/2` now returns
`{:ok, %PagedResult{}}` like `Notes.list/2` (was a bare note list). Prefer
`list/2` with `:org_id`. Documented list conventions on `ExPipedrive.Page`;
soft-documented `Pipelines.list_pipelines/1` in favor of `list_page/2` /
`stream/2` ([#86](https://github.com/blksheep80/ex_pipedrive/issues/86)).
- Refresh [AUDIT.md](AUDIT.md) as a current Pipedrive OpenAPI gap map
(implemented vs missing tags); catch-all checklist split into focused
issues ([#83](https://github.com/blksheep80/ex_pipedrive/issues/83),
[#88](https://github.com/blksheep80/ex_pipedrive/issues/88)).
### Added
- `ExPipedrive.Projects` and `ExPipedrive.ProjectBoards` (API v2): CRUD plus
cursor list/stream for projects (including archived list), and full board
lifecycle on `/api/v2/boards`; typed `ExPipedrive.Project` and
`ExPipedrive.ProjectBoard`. Phases, templates, fields, search, and archive
actions deferred
([#105](https://github.com/blksheep80/ex_pipedrive/issues/105)).
- `ExPipedrive.Tasks` (API v2, beta): `list_page/2`/`stream/2`, `get/2`,
`create/2`, `update/3`, and `delete/2` against `/api/v2/tasks`; typed
`ExPipedrive.Task`
([#106](https://github.com/blksheep80/ex_pipedrive/issues/106)).
- `ExPipedrive.DealInstallments` (API v2, Growth+ plans): `list_page/2`/`stream/2`
(requires `:deal_ids`), `create/3`, `update/4`, and `delete/3` against
`/api/v2/deals/installments` and `/api/v2/deals/:id/installments`; typed
`ExPipedrive.DealInstallment`
([#103](https://github.com/blksheep80/ex_pipedrive/issues/103)).
- `ExPipedrive.LeadFields` and `ExPipedrive.LeadSources` (API v1):
`list/2`/`stream/2` over `/api/v1/leadFields` (offset pagination,
reusing `ExPipedrive.Field`) and `list/1` over `/api/v1/leadSources`
(fixed source names via `ExPipedrive.LeadSource`)
([#104](https://github.com/blksheep80/ex_pipedrive/issues/104)).
- `ExPipedrive.NoteFields` (API v1): `list/1` (plus `list_note_fields/1`
alias) over `/api/v1/noteFields`; reuses `ExPipedrive.Field`
([#107](https://github.com/blksheep80/ex_pipedrive/issues/107)).
- `ExPipedrive.DealProducts` (API v2): `list_page/3`/`stream/3`, client-side
`get/3`, `create/3`, `update/4` (PATCH), `delete/3`, and `delete_many/3`
against `/api/v2/deals/:id/products`; typed `ExPipedrive.DealProduct`
([#102](https://github.com/blksheep80/ex_pipedrive/issues/102)).
- Expand `ExPipedrive.ActivityTypes` (API v1): `list/1` (plus
`list_activity_types/1` alias), client-side `get/2`, `create/2`,
`update/3`, `delete/2` against `/api/v1/activityTypes`; `ActivityType`
gains `order_nr`/`icon_key`/`color`
([#87](https://github.com/blksheep80/ex_pipedrive/issues/87)).
- Admin / meta APIs (v1 read-first)
([#77](https://github.com/blksheep80/ex_pipedrive/issues/77)):
`ExPipedrive.Currencies` (`list`/`get`/`get_by_code` over
`/api/v1/currencies`), `ExPipedrive.Recents` (`list` over
`/api/v1/recents`), `ExPipedrive.Roles` (list/get/assignments/pipelines/
settings), `ExPipedrive.PermissionSets` (list/get/assignments), and
`ExPipedrive.Teams` against `/api/v1/legacyTeams` (list/get/users/
list_for_user). Role/team writes deferred to `Raw`.
- `ExPipedrive.Mailbox` (API v1): `list_threads/2`, `get_thread/2`,
`list_thread_messages/2`, `get_message/3`, `update_thread/3`,
`delete_thread/2` against `/api/v1/mailbox/{mailThreads,mailMessages}`;
typed `ExPipedrive.MailThread`, `MailMessage`, and `MailMessageParty`
structs (`mail:read`/`mail:full` OAuth scopes)
([#74](https://github.com/blksheep80/ex_pipedrive/issues/74)).
- `ExPipedrive.Goals` (API v1): `list/2` (`GET /api/v1/goals/find` with
dot-notation filter options), `create/2`, `update/3`, `delete/2`, and
`get_result/4` (a goal's progress for a period) against `/api/v1/goals`;
typed `ExPipedrive.Goal` struct with string ids and plain-map
`type`/`assignee`/`expected_outcome`/`duration` (Pipedrive has no
single-goal `get` endpoint) ([#75](https://github.com/blksheep80/ex_pipedrive/issues/75)).
- `ExPipedrive.ActivityFields` and `ExPipedrive.ProductFields` (API v2):
`list_page/2`/`stream/2` over `/api/v2/{activity,product}Fields`, matching
the existing Deal/Person/Organization field modules; `ExPipedrive.Fields`
resolves custom-field hashes/labels for both
([#72](https://github.com/blksheep80/ex_pipedrive/issues/72)).
- Followers, deal participants, and organization relationships
([#73](https://github.com/blksheep80/ex_pipedrive/issues/73)):
`ExPipedrive.Followers` (API v2) manages followers on deals, persons, and
organizations via the shared `GET`/`POST`/`DELETE
/api/v2/{deals,persons,organizations}/:id/followers` shape, with cursor
`list_page/4`/`stream/4` and per-entity convenience wrappers;
`ExPipedrive.DealParticipants` is an API v1 shim over
`/api/v1/deals/:id/participants` (`ExPipedrive.DealParticipant` is distinct
from the existing `ActivityParticipant`, which decodes activity payloads);
`ExPipedrive.OrganizationRelationships` is an API v1 client over
`/api/v1/organizationRelationships` (parent/related org hierarchies).
- `ExPipedrive.ProductVariations` (API v2): `list_page/3`, `stream/3`,
`get/3` (client-side, since Pipedrive has no single-variation endpoint),
`create/3`, `update/4`, `delete/3` against the nested
`/api/v2/products/:id/variations` API; typed `ExPipedrive.ProductVariation`
struct ([#71](https://github.com/blksheep80/ex_pipedrive/issues/71)).
- `ExPipedrive.CallLogs` (API v1): `list/2`, `get/2`, `create/2`,
`add_recording/4` (multipart audio upload), `delete/2` against
`/api/v1/callLogs`; typed `ExPipedrive.CallLog` struct with string ids
(Pipedrive does not expose a call log update endpoint)
([#76](https://github.com/blksheep80/ex_pipedrive/issues/76)).
- `ExPipedrive.Files` (API v1): list/get/upload/download/update/delete plus
Google Drive `create_remote/2` and `remote_link/2`; multipart uploads via
`Tesla.Multipart` with deal/person/org/activity/product/lead/project link
fields ([#68](https://github.com/blksheep80/ex_pipedrive/issues/68)).
- Labels API ([#70](https://github.com/blksheep80/ex_pipedrive/issues/70)):
`ExPipedrive.DealLabels`, `PersonLabels`, `OrganizationLabels` manage label
definitions via the API v2 `label_ids` field-options bulk endpoints
(`POST`/`PATCH`/`DELETE /api/v2/{deal,person,organization}Fields/label_ids/options`);
`ExPipedrive.LeadLabels` is an API v1 shim over the dedicated
`/leadLabels` endpoint; `ExPipedrive.Labels` facade delegates across all
four. Typed `ExPipedrive.Label` struct. Assigning/clearing labels on an
entity remains a normal `label_ids` write via that entity's own
`update/3` — there is no separate assign/clear endpoint.
- `ExPipedrive.Filters` (API v1): `list/2`, `get/2`, `create/2`, `update/3`,
`delete/2` against `/api/v1/filters`; `conditions` accepted as a plain map
([#69](https://github.com/blksheep80/ex_pipedrive/issues/69)).
- `ExPipedrive.Users` (API v1): `me/1`, `get/2`, `list/2` (offset pagination),
plus fake-server fixtures and tests for `me`/`get`/`list`/`find_users_by_name`
([#67](https://github.com/blksheep80/ex_pipedrive/issues/67)).
## [0.1.0] - 2026-07-31
First Hex release of **ExPipedrive**, a v2-first fork of
[LineDrive](https://github.com/tmecklem/line_drive).
### Added
#### Client foundation
- Pipedrive API **v2** client foundation (`Client`, `Request`, default `/api/v2`;
explicit `api_version: :v1` where needed)
- Header API token auth (`x-api-token`); legacy query auth via `auth: :query`
- Structured `ExPipedrive.Error` / `Response` mapping
- `Page` / `Cursor.stream` for cursor pagination (limit clamped to 500)
- OAuth `Token` bundles, `ensure_fresh`, pluggable `TokenStore` (no Ecto in core)
- Rate-limit aware retry middleware + request telemetry
(`ExPipedrive.Middleware.Retry`, `ExPipedrive.Middleware.Telemetry`,
`ExPipedrive.RateLimit`); Client opts `:retry`, `:telemetry`, `:middleware`
- `ExPipedrive.Resource` behaviour + CRUD/list/stream helpers; `Products` and
`Stages` adopt the pattern
- `ExPipedrive.Raw.request/4` escape hatch for unsupported endpoints
#### Resources (API v2 unless noted)
- Deals and Persons: list/stream (cursor), get, create, update; deal delete
- Organizations, Activities, Pipelines, Stages, Products: list/stream, get,
create, update, delete (product variations deferred)
- Search: `ExPipedrive.Search` over `/api/v2/itemSearch` with cursor pages/stream
and typed `SearchResult`
- Deal, Person, and Organization Fields list/page/stream plus
`ExPipedrive.Fields` for resolving custom-field hashes and labels
- Leads / Notes: explicit **API v1** shims with map-based create helpers and
`get/2`, `create/2`, `list/2` aliases
- Webhooks: `ExPipedrive.Webhooks` subscription list/create/delete (**API v1**
management); `Webhook.Event` / `Webhook.Handler` inbound surface (optional Plug;
Basic auth; package extract deferred)
#### Docs & tooling
- Fake Pipedrive server fixtures for v2 resources used in tests
- MVP README flows: stream open deals; create person then deal
- CI matrix, ExDoc, Hex publish-on-release workflow
### Changed
- Package / OTP app / modules rebranded from LineDrive → `ex_pipedrive` /
`ExPipedrive`
- Core deps slimmed (Timex removed; Plug optional for webhooks only)
- Silent OTP Application/Registry coupling removed
### Migrating from LineDrive
1. Depend on `{:ex_pipedrive, "~> 0.1.0"}` instead of `:line_drive`.
2. Rename modules `LineDrive.*` → `ExPipedrive.*`.
3. Prefer `ExPipedrive.client/2` (header token) and v2 helpers
(`Deals.stream/2`, `Deals.create/2`, `Persons.create/2`, …) over legacy v1
list APIs.
4. OAuth: persist `ExPipedrive.Oauth.Token` via your `TokenStore` implementation;
build clients with `Client.from_token/2` or `from_token_store/4`.
5. Use `ExPipedrive.Raw.request/4` for endpoints not yet wrapped.
[unreleased]: https://github.com/blksheep80/ex_pipedrive/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/blksheep80/ex_pipedrive/releases/tag/v0.2.0
[0.1.0]: https://github.com/blksheep80/ex_pipedrive/releases/tag/v0.1.0