Create a lead
POST/api/v1/leads
- Since
- 1.0.0
- Auth
- Bearer key
- operationId
- createLead
Stores a patient enquiry from your own form in the clinic's CRM. It follows the same path as an enquiry from the clinic's site: reception gets an e-mail, and a configured webhook receives lead.created. Call it from your server: the key is a secret. Field names are matched loosely (Polish and English aliases, any letter case), unknown fields are kept, and a lead without a phone or e-mail is stored with a warning unless you ask for ?strict=1.
Request
Send the clinic's key in Authorization: Bearer (see API keys).
Query parameters
- strictstringsince 1.3.0
Reject a lead without a phone and e-mail (400 missing_contact), and one with an invalid
timeRequest(400 invalid_time_request), instead of storing it withwarnings.One of
1true
Body application/json
The enquiry as JSON.
- namestring
Patient's name. Aliases: full_name, fullname, imie, imię, imie_nazwisko.
- phonestring
Phone number. Aliases: tel, telefon, phone_number, numer.
- emailstring
E-mail address. Aliases: e-mail, mail.
- messagestring
The patient's message. Aliases: wiadomosc, wiadomość, msg, comment, opis.
- utm_sourcestring
Campaign source, stored with the lead's tracking data.
- utm_mediumstringsince 1.1.0
Campaign medium.
- utm_campaignstringsince 1.1.0
Campaign name.
- gclidstring
Google Ads click id.
- fbclidstring
Meta click id.
- privacyNoticeVersionstringsince 1.7.0
The
versionof the information clause your form showed (from GET /privacy). Stored with the submission and its time; without it the submission is stored without a clause version. - marketingConsentbooleansince 1.7.0
true when the patient ticked the optional marketing box (label
marketingTextfrom GET /privacy). Recorded for SMS (when there is a phone) and e-mail (when there is an address) together with the clause version. It counts only with a validprivacyNoticeVersion: without it no consent is recorded and an earlier opt-out stays. For a returning patient booking an appointment, the e-mail consent covers only the address stored with their lead (or the given address when the lead had none). Omit it, or send false, when the box was not ticked. - timeRequestTimeRequestInputsince 1.9.0
A request for a time: the patient's preferred windows, for clinics that keep their calendar in their own system.
Fields (5)
- windowsTimeWindow[]required
1 to 3 preferred windows.
Fields of each item (3)
- datestring (date)required
Day, YYYY-MM-DD.
- fromstringrequired
Start, HH:MM.
- tostringrequired
End, HH:MM.
- serviceSlugstring
Slug of a published treatment (GET /services).
- doctorSlugstring
Slug of a published team member (GET /doctors); omit for any doctor.
- serviceNamestring
Treatment name as text, when your frontend has no slug.
- doctorNamestring
Doctor's name as text, when your frontend has no slug.
Polish field names are accepted too
{
"imie": "Anna Kowalska",
"telefon": "+48 600 100 200",
"wiadomosc": "Proszę o kontakt."
}A lead with a request for a time (API 1.9.0)
{
"name": "Jan Nowak",
"phone": "+48 600 100 200",
"timeRequest": {
"windows": [
{
"date": "2026-10-05",
"from": "08:00",
"to": "12:00"
},
{
"date": "2026-10-06",
"from": "15:00",
"to": "18:00"
}
],
"serviceSlug": "higienizacja",
"doctorSlug": "maria-zielinska"
}
}curl -X POST "https://app.pozyskajpacjenta.pl/api/v1/leads" \
-H "Authorization: Bearer $PP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Anna Kowalska",
"phone": "+48 600 100 200",
"email": "anna.kowalska@example.com",
"message": "Proszę o kontakt w sprawie higienizacji.",
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "higienizacja-jesien",
"formularz": "landing-higienizacja"
}'import { PozyskajPacjentaClient } from "@pozyskajpacjenta/sdk";
const client = new PozyskajPacjentaClient({
apiKey: process.env.PP_API_KEY ?? "",
baseUrl: "https://app.pozyskajpacjenta.pl",
});
const { leadId, warnings } = await client.createLead({
name: "Anna Kowalska",
phone: "+48 600 100 200",
email: "anna.kowalska@example.com",
message: "Proszę o kontakt w sprawie higienizacji.",
utm_source: "google",
utm_medium: "cpc",
utm_campaign: "higienizacja-jesien",
formularz: "landing-higienizacja",
});<?php
$body = array(
'name' => 'Anna Kowalska',
'phone' => '+48 600 100 200',
'email' => 'anna.kowalska@example.com',
'message' => 'Proszę o kontakt w sprawie higienizacji.',
'utm_source' => 'google',
'utm_medium' => 'cpc',
'utm_campaign' => 'higienizacja-jesien',
'formularz' => 'landing-higienizacja',
);
$response = wp_remote_post(
'https://app.pozyskajpacjenta.pl/api/v1/leads',
array(
'headers' => array(
'Authorization' => 'Bearer ' . getenv( 'PP_API_KEY' ),
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( $body ),
'timeout' => 10,
)
);
if ( is_wp_error( $response ) ) {
error_log( $response->get_error_message() );
return;
}
$status = wp_remote_retrieve_response_code( $response );
$data = json_decode( wp_remote_retrieve_body( $response ), true );Response
200The lead was stored.
- okbooleanrequired
Always true.
- leadIdstringrequired
Id of the lead in the clinic's CRM.
- warningsstring[]since 1.3.0
Only when there is a warning. missing_contact: stored, but without a phone or e-mail, so nobody can call back. invalid_time_request: stored without the request for a time, because
timeRequestfailed validation.One of
missing_contactinvalid_time_request - timeRequestLeadTimeRequestsince 1.9.0
The stored request for a time, when you sent a valid one.
Fields (5)
- idstringrequired
Id of the request.
- statusstringrequired
pending until reception acts; confirmed (a time was confirmed), proposed (reception proposed another time) or cancelled.
One of
pendingconfirmedproposedcancelled - windowsTimeWindow[]required
The windows that were stored.
Fields as in TimeWindow above.
- servicestring | nullrequired
Name of the matched treatment, or the text you sent.
- doctorstring | nullrequired
The doctor (matched or as sent); null for any doctor.
Lead stored
{
"ok": true,
"leadId": "rS2bIhedVSStSQLc7DL_T"
}Stored without a phone or e-mail
{
"ok": true,
"leadId": "Sj2mY5tuT1UZP0QfgdvLH",
"warnings": [
"missing_contact"
]
}Stored with the request for a time
{
"ok": true,
"leadId": "PydHg_es-Kqw5bs1Y0-wu",
"timeRequest": {
"id": "tqf-L61QFuCLO2ZXCM1OL",
"status": "pending",
"windows": [
{
"date": "2026-10-05",
"from": "08:00",
"to": "12:00"
},
{
"date": "2026-10-06",
"from": "15:00",
"to": "18:00"
}
],
"service": "Higienizacja",
"doctor": "lek. dent. Maria Zielińska"
}
}Stored without an invalid request for a time
{
"ok": true,
"leadId": "J_ashz8nW11Am5rfS90rB",
"warnings": [
"invalid_time_request"
]
}Errors
| Status | code | When |
|---|---|---|
| 400 | invalid_json | The body is not JSON |
| 400 | invalid_body | The JSON is not an object |
| 400 | missing_contact | ?strict=1 and no phone or e-mail |
| 400 | invalid_time_request | ?strict=1 and an invalid request for a time |
| 401 | No Authorization header | |
| 401 | Unknown or rotated key | |
| 429 | Over 120 requests in this minute |
Branch on the status and code, never on the Polish error text. All statuses in Errors.
{
"error": "invalid JSON",
"code": "invalid_json"
}{
"error": "treść musi być obiektem JSON z danymi leada",
"code": "invalid_body"
}{
"error": "wymagany kontakt: telefon (pole phone/tel/telefon/phone_number/numer, min. 6 cyfr) albo e-mail (pole email/e-mail/mail)",
"code": "missing_contact"
}{
"error": "timeRequest: windows.0.date: nie ma takiego dnia w kalendarzu",
"code": "invalid_time_request"
}{
"error": "missing Authorization: Bearer <klucz z panelu Ustawienia>"
}{
"error": "invalid key"
}{
"error": "przekroczono limit 120 zapytań/min"
}Rate limit
- 120 requests per minute per key, shared by every endpoint.
- Every response to a valid key carries
RateLimit-RemainingandRateLimit-Reset; see Rate limits.
SDK method
client.createLead(lead: LeadInput, options?: { strict?: boolean }): Promise<LeadCreated>
Throws ApiError (400) with code; with { strict: true } also missing_contact and invalid_time_request.
The TypeScript tab above uses it. Install and errors: TypeScript SDK.