Skip to main content
The public API is one versioned contract, /api/v1, for getting a lead into a workspace from your own systems, authenticated by a key instead of a signed-in session.
Where
SettingsAPI & Integrations
Your role needs
Update access to Lead intake API update-companies. Admin have it by default.
Authenticated by a workspace API key, not by a signed-in session. Creating a key needs the same permission as the rest of the API settings tab.

What’s part of the contract

Two surfaces are stable enough to build on, and this help center documents both: the versioned group at /api/v1, and the public endpoints under /api/public/…: the Web Forms submissions endpoint, the website tracker beacon and the free tools. Those are the shapes that cannot quietly change. Nothing else is. The rest of /api/ is the app talking to itself, and it can change without notice however solid it looks in a network tab.

Authenticating a request

Authenticate with an Authorization: Bearer <key> header, or X-API-Key: <key> if your HTTP client reserves the first one for something else. There’s no workspace parameter to pass: a key is minted for one workspace, and every request resolves that workspace from the key alone, so an integration can never address a workspace it wasn’t issued a key for. A key’s prefix says which environment issued it. Keys look like plk_test_a1B2c3D4_… everywhere except production, where they begin plk_live_. A key only works against the environment that minted it, so one pasted into the wrong environment is rejected on the first request rather than writing somewhere you didn’t mean.

Scopes

A key carries one or more scopes, chosen when it’s created. The create-key dialog calls them Permissions, and it won’t let you create a key with none. There are three. tags:read lists your tags, leads:read reads a lead back, and leads:write creates and updates them. Each route checks its own scope, so granting one never grants another. leads:write and tags:read are ticked for you when the dialog opens, which is enough to send a lead in; leads:read is something you add on purpose. One route needs no scope at all: GET /me is a health check any key can call, whatever it’s scoped for, and it’s the fastest way to confirm a credential works before you build anything else on it. Call an endpoint your key isn’t scoped for and you get a 403 naming the exact scope that is missing, not a blanket unauthorized.

Rate limits, and never from a browser

Each key gets its own budget of 300 requests a minute. It is counted against the key, not against the address the request came from, so an integration you run on shared or serverless infrastructure is measured on its own traffic. A batch call counts as one request, however many leads it carries.
Cross-origin requests are not restricted by domain, so a browser can call this API directly. That is exactly why a key must never ship in client-side code. Anyone who opens your page’s network tab or reads its JavaScript can read the key and use it exactly as you can, including to write into your workspace. Call the API from a server, a serverless function or an automation tool, never from a script that runs in a visitor’s browser. If you need a browser to send you leads, use Web Forms or the tracker beacon: those carry a public per-form or per-tracker token instead of a key, and check the domain calling them.
Most of this also lives inside the product, on the same settings tab as your keys and directly below them: a Send a lead card pre-filled with your workspace’s own tags, a Try it panel that runs that exact request against your workspace once you paste a key into it, and a Fields table. Dry run is on when the panel loads, so the first request you send validates and previews without saving anything. Every topic below has its own page here; the panel is the same material without leaving the app.

Where each part is explained

Authentication

Every header and failure mode for a request, and what each one means.

POST /leads

The request body, duplicate handling, dry run, and every field the response returns.

POST /leads/batch

Send many leads in one call and read the outcome of each row on its own.

Lead fields reference

Every field POST /leads accepts, grouped, with the limits on the repeating ones.

GET /me, GET /tags and GET /leads/{id}

Confirm which workspace a key belongs to, list the tags you can apply, and read a lead back.

Idempotency, deduplication and rate limits

Retry safely, how an existing lead gets recognized, and what happens when you send too fast.

Code samples

Ready-to-paste requests in cURL, JavaScript, Python and PHP, plus the raw JSON body.

Web Forms: capture script and submissions endpoint

Install the embed snippet, or post submissions to the endpoint yourself.

Website tracker beacon

What the tracking snippet sends, and what its errors mean.

Outbound webhooks

How a lead leaves the platform: a connector and a workflow block, not a subscribable event feed.

Free tools

Public tools anyone can use without a key, and how they differ from the API.

Create, roll and revoke API keys

Where a key actually comes from, and what changes the moment you revoke one.

Quickstart: send your first lead

Check the key, post a lead as a dry run, then send it for real.

Errors reference

Every status the API returns and what to change before you retry.