1. O API
Element External Integration API to interfejs REST udostępniający dane i operacje rekrutacyjne z Twojego konta Element systemom zewnętrznym. Pozwala m.in. na:
- pobranie listy projektów rekrutacyjnych, ich etapów i kandydatów,
- pobranie pełnego profilu kandydata oraz odpowiedzi udzielonych przez niego w formularzu aplikacyjnym,
- przyjęcie aplikacji od kandydata z zewnętrznego kanału (np. voice bota, formularza partnerskiego),
- zapisanie publicznej notatki na profilu kandydata (np. transkrypcji rozmowy telefonicznej).
API zostało zaprojektowane jako uniwersalny mechanizm — ten sam kontrakt obsługuje voice botty, systemy ATS/HRIS partnerów, automatyzacje wewnętrzne i przepływy danych. Wszystkie operacje są audytowane po stronie Element, a autor zmian widoczny jest w panelu jako konto API zidentyfikowane nazwą Twojego systemu (np. API: voicebot).
Charakterystyka
- Architektura: REST, JSON.
- Wersjonowanie w ścieżce:
/api/v1-beta/... - Uwierzytelnianie: HTTP Basic per credential, hasła hash-owane po stronie Element.
- Multi-tenancy: dostęp izolowany do jednego tenanta (Twojego konta Element), identyfikowanego subdomeną.
- Idempotencja: brak na poziomie API; integrator jest odpowiedzialny za deduplikację (szczegóły w sekcji 10).
2. Przed startem
Aby skorzystać z API, potrzebujesz:
- Aktywnego konta Element Twojej organizacji.
- Uprawnień administratora (administrator tenanta) — lub przekazania tego dokumentu osobie posiadającej te uprawnienia.
- Dostępu do panelu Element na Twojej subdomenie (
https://<twoj-tenant>.elementapp.ai). - Wygenerowanego zestawu credentials dla każdego systemu, który będzie korzystał z API (sekcja Dostęp do API w panelu).
Każdy system zewnętrzny (np. voice bot, system kadrowy) powinien otrzymać osobny zestaw credentials — ułatwia to audyt, ograniczenie blast radius w razie wycieku i niezależną rotację haseł.
Co dostajesz przy tworzeniu credentials
Po utworzeniu w panelu otrzymujesz:
- Login w deterministycznym formacie:
api+<nazwa-systemu>@<twoj-tenant>.local
Przykład:api+voicebot@acme.local - Hasło — losowo wygenerowane, pokazywane wyłącznie raz, w momencie utworzenia.
Hasło nie jest możliwe do odzyskania. Zapisz je w bezpiecznym miejscu (vault, sejf sekretów, GitHub/GitLab Secrets) od razu po utworzeniu. Jeśli zgubisz hasło, wygeneruj nowe przyciskiem Regeneruj hasło w panelu — poprzednie zostanie unieważnione natychmiast.
3. Pierwsze wywołanie
Po wygenerowaniu credentials zweryfikuj połączenie:
curl --user "api+voicebot@acme.local:<twoje-haslo>" \
https://acme.elementapp.ai/api/v1-beta/health
Oczekiwana odpowiedź:
{ "status": "ok", "apiVersion": "v1-beta" }
Jeśli widzisz HTTP 401, sprawdź czy:
- login i hasło są skopiowane dokładnie tak jak je otrzymałeś (uważaj na końcowe spacje, znak
+i@przekazuj bez kodowania URL), - używasz właściwej subdomeny tenanta (
https://<twoj-tenant>.elementapp.ai, niehttps://elementapp.aiani inną domenę), - konto API nie zostało dezaktywowane (sprawdź status w panelu).
4. Uwierzytelnianie
API używa HTTP Basic Authentication zgodnie z RFC 7617.
| Element | Wartość |
|---|---|
| Schemat | Basic |
| Realm | External Integration API |
| Username | Twój login API |
| Password | Hasło wygenerowane w panelu |
| Nagłówek | Authorization: Basic base64(login:hasło) |
Identyfikacja tenanta
Tenant jest identyfikowany wyłącznie na podstawie subdomeny w URL (acme.elementapp.ai → tenant acme). Credentials są zlocked do jednego tenanta — próba użycia ich na cudzej subdomenie zwróci 401 Unauthorized lub 403 Forbidden.
Konto API w panelu
Twoje konto API jest widoczne w panelu Element jako:
- użytkownik techniczny z rolą Integracja API,
- nieliczone do limitu aktywnych konsultantów,
- ukryte w listach i dropdownach konsultantów (nie zaśmieca widoków biznesowych),
- widoczne wyłącznie w sekcji Dostęp do API.
Wszystkie akcje zapisu wykonane przez API są podpisywane Twoim kontem (API: <nazwa-systemu>) — konsultanci tenanta widzą jasno, że dana aplikacja czy notatka pochodzi z systemu zewnętrznego.
5. Format danych
5.1. Content negotiation
- Odpowiedzi: wszystkie zwracane są jako
application/json; charset=utf-8. - Requesty z body: akceptowane formaty to
application/x-www-form-urlencodedorazmultipart/form-data(ten ostatni wymagany dla uploadu CV wPOST /apply).
5.2. Envelope odpowiedzi
Sukces zwraca obiekt z polem data:
{
"data": { /* obiekt lub tablica encji */ },
"meta": { /* obecne tylko dla list paginowanych */ }
}
Błąd zwraca obiekt z polem error:
{
"error": {
"code": "snake_case_identifier",
"message": "Human-readable description (English)."
}
}
Dla walidacji (HTTP 400) blok error zawiera dodatkowo mapę problemów per pole:
{
"error": {
"code": "validation_failed",
"fields": {
"email": [
{ "message": "Ta wartość nie jest prawidłowym adresem email.", "code": "bd79c0ab-ddba-46cc-a703-a7a4b08de310" }
]
}
}
}
Komunikaty walidacji są lokalizowane (w języku domyślnym Twojego tenanta). Identyfikatory pól JSON pozostają w angielskim — to kontrakt API, język niezależny.
5.3. Paginacja
Endpointy zwracające listy przyjmują parametry query:
| Parametr | Typ | Domyślnie | Zakres | Opis |
|---|---|---|---|---|
page | integer | 1 | ≥ 1 | Numer strony, indeksowany od 1. |
perPage | integer | 20 | 1 – 100 | Liczba elementów na stronie. Wartości spoza zakresu są przycinane. |
Każda odpowiedź zawiera blok meta:
{
"data": [ /* ... */ ],
"meta": { "page": 1, "perPage": 20, "total": 42 }
}
5.4. Daty i czas
Format ISO 8601 z offsetem UTC: 2026-05-28T12:34:56+00:00. Daty bez czasu (np. okresy zatrudnienia w profilu kandydata) używają formatu YYYY-MM-DD.
5.5. Identyfikatory
Wszystkie identyfikatory zasobów (projectId, candidateId, stageId, noteId, …) to UUID v4 w formacie kanonicznym, np. ff315ea1-c056-472d-aefe-b99f3d41e5c0.
5.6. Lokalizacja treści
Etykiety pytań i wartości wyborów w endpoincie zwracającym wypełniony formularz aplikacyjny (GET /projects/{projectId}/candidates/{candidateId}/application-form) honorują query param locale (np. pl, en, de). Jeśli żądany język nie jest skonfigurowany w formularzu, zwracana jest wartość w domyślnym języku formularza.
6. Limity wywołań
| Parametr | Wartość |
|---|---|
| Limit domyślny | 70 wywołań / minutę per credential (60 baseline + 10 burst) |
| Sustained throughput | ~1.2 wywołania / sekundę |
| Klucz limitu | Twoje konto API |
Po przekroczeniu limitu API odpowiada 429 Too Many Requests:
{ "error": { "code": "rate_limit_exceeded", "message": "Too Many Requests" } }
Zalecenia
- Po
429zastosuj backoff (rekomendowany exponential): start od 1 sekundy, podwajaj do maksymalnego 30 s. - Rozkładaj batch'e w czasie (
for-each + sleep), zamiast strzelać wszystkim na raz. - Jeśli regularnie potrzebujesz wyższego limitu, skontaktuj się z supportem Element — limit można podnieść per credential.
7. Obsługa błędów
| HTTP | error.code | Znaczenie |
|---|---|---|
200 OK | — | Sukces (GET). |
201 Created | — | Sukces (POST, zasób utworzony — odpowiedź zawiera identyfikator). |
400 Bad Request | validation_failed | Nieprawidłowy lub niepełny request. error.fields opisuje problemy per pole. |
401 Unauthorized | — | Brak nagłówka Authorization, nieprawidłowy login lub hasło. Body puste, nagłówek WWW-Authenticate: Basic. |
403 Forbidden | — | Konto bez wymaganej roli lub dezaktywowane. Body puste. |
404 Not Found | project_not_found | Projekt nie istnieje, jest zarchiwizowany lub nie należy do Twojego tenanta. |
404 Not Found | candidate_not_found | Kandydat nie istnieje, jest zarchiwizowany, nie należy do Twojego tenanta lub zażądał usunięcia danych (RODO). |
404 Not Found | application_form_not_found | Projekt nie ma podpiętego formularza aplikacyjnego lub kandydat nie wypełnił go w tym projekcie. |
409 Conflict | no_application_stage | POST /apply: projekt nie ma skonfigurowanego etapu typu „aplikacja". |
429 Too Many Requests | rate_limit_exceeded | Przekroczony limit (sekcja 6). |
5xx | — | Awaria po stronie Element. Spróbuj ponownie z backoff. Jeśli powtarza się dłużej niż kilka minut, skontaktuj się z supportem. |
8. Referencja endpointów
Wszystkie endpointy poprzedzone są base URL: https://<twoj-tenant>.elementapp.ai/api/v1-beta.
8.1. Health check
Weryfikacja dostępności API i poprawności credentials. Nie zwraca danych biznesowych.
Odpowiedź 200:
{ "status": "ok", "apiVersion": "v1-beta" }
8.2. Lista projektów
Zwraca paginowaną listę aktywnych projektów rekrutacyjnych Twojego tenanta.
Odpowiedź 200:
{
"data": [
{
"id": "ff315ea1-c056-472d-aefe-b99f3d41e5c0",
"name": "Senior PHP Developer",
"positionName": null,
"isRemote": true,
"statusId": "73f09ef2-77f7-4b12-bbed-5beb6131dbea",
"createdAt": "2021-10-31T11:42:24+00:00"
}
],
"meta": { "page": 1, "perPage": 20, "total": 8 }
}
| Pole | Typ | Opis |
|---|---|---|
id | UUID | Identyfikator projektu — używaj go w pozostałych endpointach. |
name | string | Nazwa projektu. |
positionName | string | null | Tytuł stanowiska, jeśli różny od nazwy projektu. |
isRemote | bool | Czy stanowisko jest zdalne. |
statusId | UUID | Identyfikator statusu projektu. |
createdAt | datetime | Data utworzenia projektu. |
8.3. Szczegóły projektu
Zwraca pełne dane projektu wraz z listą etapów.
Odpowiedź 200:
{
"data": {
"id": "ff315ea1-c056-472d-aefe-b99f3d41e5c0",
"name": "Senior PHP Developer",
"positionName": null,
"isRemote": true,
"statusId": "73f09ef2-77f7-4b12-bbed-5beb6131dbea",
"createdAt": "2021-10-31T11:42:24+00:00",
"responsibilities": "<ul><li>...</li></ul>",
"requirements": "<ul><li>...</li></ul>",
"offer": "<ul><li>...</li></ul>",
"minimumSalary": 12000,
"maximumSalary": 18000,
"numberOfVacancies": 4,
"stages": [
{ "id": "...", "name": "Linkedin — zaproszeni", "isApplicationType": false },
{ "id": "...", "name": "Aplikacja", "isApplicationType": true },
{ "id": "...", "name": "Rozmowa", "isApplicationType": false }
]
}
}
| Pole | Typ | Opis |
|---|---|---|
responsibilities, requirements, offer | string HTML | null | Treści marketingowe — mogą zawierać tagi HTML (<ul>, <li>, <p>, …). |
minimumSalary, maximumSalary | int | null | Widełki wynagrodzenia w walucie konfigurowanej w tenancie. 0 oznacza „nie ustawione". |
numberOfVacancies | int | null | Liczba wakatów. |
stages | array | Lista etapów rekrutacji w projekcie. |
stages[].isApplicationType | bool | true dla etapu, w którym lądują aplikacje z POST /apply. |
Możliwe błędy: 401, 404 project_not_found, 429.
8.4. Etapy projektu
Skrócona wersja — tylko lista etapów (ten sam shape co data.stages z /projects/{projectId}).
{
"data": [
{ "id": "515806af-0470-40ef-84ed-4ce889d103ed", "name": "Aplikacja", "isApplicationType": true },
{ "id": "0f23ea11-fa6d-42b6-97c2-8356e8d1827e", "name": "Rozmowa", "isApplicationType": false }
]
}
Możliwe błędy: 401, 404 project_not_found, 429.
8.5. Kandydaci w projekcie
Paginowana lista kandydatów uczestniczących w projekcie. Opcjonalnie filtruj po etapie.
{
"data": [
{
"id": "87c42b09-4cde-47db-90f9-963928fedbdb",
"firstName": "Anna",
"lastName": "Nowak",
"email": "anna.nowak@example.com",
"stageId": "515806af-0470-40ef-84ed-4ce889d103ed",
"isRejected": false,
"addedAt": "2026-05-27T17:57:52+00:00"
}
],
"meta": { "page": 1, "perPage": 20, "total": 17 }
}
| Pole | Typ | Opis |
|---|---|---|
id | UUID | Identyfikator kandydata. |
firstName, lastName | string | null | Imię i nazwisko. |
email | string | Email ("" możliwe dla kandydatów zaimportowanych historycznie). |
stageId | UUID | Aktualny etap kandydata w tym projekcie. |
isRejected | bool | Czy kandydat został odrzucony w tym projekcie. |
addedAt | datetime | Data dodania do projektu. |
Możliwe błędy: 401, 404 project_not_found, 429.
8.6. Metadane formularza aplikacyjnego projektu
Zwraca metadane formularza aplikacyjnego podpiętego do projektu — bez treści pytań.
{
"data": {
"id": "78132d76-ec2e-4fb4-a069-8688b9c4d2e2",
"projectId": "ff315ea1-c056-472d-aefe-b99f3d41e5c0",
"schemaId": "d6aea4da-b701-4504-aaa7-58062c302493",
"type": "custom",
"isEmailRequired": true,
"isPhoneNumberRequired": true,
"isCvRequired": true
}
}
| Pole | Typ | Opis |
|---|---|---|
id | UUID | Identyfikator instancji formularza. |
schemaId | UUID | Identyfikator wersji schematu (zmiana = nowa wersja pytań). |
type | string | "custom", "default", "signal". |
isEmailRequired, isPhoneNumberRequired, isCvRequired | bool | Czy pole jest wymagane. Użyj do walidacji przed wysłaniem POST /apply. |
Pełna struktura formularza (pytania, opcje, walidacje) jest świadomie pomijana w v1-beta — zewnętrzne systemy mają używać własnego flow do zbierania danych i wywołać POST /apply z minimalnym zestawem (patrz 8.11).
Możliwe błędy: 401, 404 project_not_found, 404 application_form_not_found, 429.
8.7. Odpowiedzi kandydata w formularzu
Zwraca wypełniony formularz aplikacyjny konkretnego kandydata w konkretnym projekcie — każdą sekcję wraz z pytaniami i odpowiedziami.
{
"data": {
"projectId": "ff315ea1-c056-472d-aefe-b99f3d41e5c0",
"candidateId": "87c42b09-4cde-47db-90f9-963928fedbdb",
"applicationFormId": "78132d76-ec2e-4fb4-a069-8688b9c4d2e2",
"submittedAt": "2026-05-27T17:57:48+00:00",
"formVersion": "2026-04-09T12:55:57+00:00",
"locale": "pl",
"sections": [
{
"title": "Pytania aplikacyjne",
"items": [
{
"questionId": "ae8a8a8a-c8c2-48d3-9e2d-b3c6ba7f2a35",
"label": "Jak oceniasz swoją umiejętność programowania w PHP?",
"type": "scale",
"subtype": null,
"answer": "1"
},
{
"questionId": "15a281d6-0923-4a57-bf22-4e82622e324c",
"label": "Osoba polecająca",
"type": "referral",
"subtype": null,
"answer": { "name": null, "email": null, "surname": null }
}
]
}
]
}
}
| Pole | Typ | Opis |
|---|---|---|
applicationFormId | UUID | Identyfikator wersji formularza w momencie złożenia. |
submittedAt | datetime | Kiedy kandydat złożył aplikację. |
formVersion | datetime | Sygnatura czasowa wersji schematu pytań. |
locale | string | Faktycznie zwrócony język (po ewentualnym fallbacku). |
sections[].items[].type | string | oneAnswer, manyAnswers, scale, yesNo, text, number, date, referral, … |
sections[].items[].subtype | string | null | Dodatkowy kontekst semantyczny (weekDay, hourFrom, hourTo, …). |
sections[].items[].answer | string | int | bool | array | null | Odpowiedź; typ zależy od type/subtype. null = brak odpowiedzi. |
Pomijane celowo:
- Wewnętrzny scoring kandydata (
obtainedScore,isDisqualified) — to mechanizm wewnętrzny Element. - Pytania typu „upload pliku" — jeśli sekcja zawiera wyłącznie pytania plikowe, jest pomijana w odpowiedzi.
Możliwe błędy: 401, 404 project_not_found, 404 application_form_not_found, 429.
8.8. Profil kandydata
Zwraca pełen profil kandydata.
{
"data": {
"id": "87c42b09-4cde-47db-90f9-963928fedbdb",
"firstName": "Anna",
"lastName": "Nowak",
"email": "anna.nowak@example.com",
"phoneNumber": "+48500111222",
"financialExpectation": 12000,
"optimalFinancialExpectation": 15000,
"isRemote": false,
"createdAt": "2026-05-27T17:57:48+00:00",
"experience": [
{
"companyName": "Acme Sp. z o.o.",
"jobPosition": "Senior PHP Developer",
"description": "Architektura mikroserwisów, …",
"employedSince": "2020-01-01",
"employedTo": null
}
],
"education": [
{
"educationFacility": "Politechnika Warszawska",
"specialization": "Informatyka",
"attendedSince": "2014-10-01",
"attendedTo": "2019-06-30"
}
],
"skills": [
{ "name": "PHP", "experienceInYears": 8 }
],
"languageSkills": [
{ "languageId": "en", "cefrLevel": "C1" }
],
"certifications": [
{ "certificationId": "...", "name": "AWS Certified Developer" }
]
}
}
| Pole | Typ | Opis |
|---|---|---|
firstName, lastName | string | null | Imię i nazwisko. |
email, phoneNumber | string | null | Dane kontaktowe. |
financialExpectation, optimalFinancialExpectation | int | null | Minimalne / oczekiwane wynagrodzenie w walucie tenanta. |
isRemote | bool | Czy kandydat preferuje pracę zdalną. |
experience[] | array | Doświadczenie zawodowe (daty YYYY-MM-DD, employedTo: null = nadal zatrudniony). |
education[] | array | Wykształcenie. |
skills[].experienceInYears | int | Lata doświadczenia z daną umiejętnością. |
languageSkills[].cefrLevel | string | Poziom CEFR (A1–C2). |
Wewnętrzny opis konsultanta (pole description w CRM Element) jest świadomie wyłączony z odpowiedzi — to prywatne notatki rekruterów, nie podlegają udostępnianiu przez API.
Możliwe błędy: 401, 404 candidate_not_found, 429.
8.9. Projekty kandydata
Paginowana lista aktywnych projektów, w których kandydat bierze udział, posortowana po dacie dodania (malejąco).
{
"data": [
{
"projectId": "ff315ea1-c056-472d-aefe-b99f3d41e5c0",
"projectName": "Senior PHP Developer",
"stageId": "515806af-0470-40ef-84ed-4ce889d103ed",
"isRejected": false,
"addedAt": "2026-05-27T17:57:52+00:00"
}
],
"meta": { "page": 1, "perPage": 20, "total": 1 }
}
Możliwe błędy: 401, 404 candidate_not_found, 429.
8.10. Publiczne notatki kandydata
Paginowana lista publicznych notatek kandydata. Notatki prywatne nie są nigdy udostępniane przez API.
| Parametr query | Typ | Opis |
|---|---|---|
page, perPage | int | Paginacja. |
projectId | UUID | Opcjonalnie — zawęź do notatek dodanych w kontekście wskazanego projektu. |
{
"data": [
{
"id": "898a1822-ac9e-4473-9f56-e0a8542676e7",
"content": "Kandydat zaakceptował ofertę wstępną.",
"createdAt": "2026-05-28T08:41:22+00:00",
"createdBy": "a7a58f75-25b8-462b-813e-0248265603e8",
"projectId": "ff315ea1-c056-472d-aefe-b99f3d41e5c0"
}
],
"meta": { "page": 1, "perPage": 20, "total": 1 }
}
| Pole | Typ | Opis |
|---|---|---|
createdBy | UUID | Identyfikator autora notatki w Element (Twoje konto API albo konto konsultanta). |
projectId | UUID | null | null jeśli notatka jest na profilu kandydata, nie powiązana z projektem. |
Możliwe błędy: 401, 404 candidate_not_found, 429.
8.11. Złożenie aplikacji
Tworzy nowego kandydata i dodaje go do projektu na etapie typu „aplikacja".
Każde wywołanie tworzy nowego kandydata. API nie deduplikuje po adresie email. Jeśli chcesz uniknąć duplikatów, sprawdź najpierw /projects/{projectId}/candidates po stronie Twojego systemu.
Content-Type: multipart/form-data (z CV) lub application/x-www-form-urlencoded (bez CV).
Pola requestu:
| Pole | Wymagane | Typ | Limit | Opis |
|---|---|---|---|---|
firstName | tak | string | 255 znaków | Imię. |
lastName | tak | string | 255 znaków | Nazwisko. |
email | tak | string | 255 znaków | Email, walidowany. |
phoneNumber | nie | string | 32 znaki | Telefon (format swobodny). |
source | nie | string | 64 znaki | Identyfikator źródła aplikacji w Twoim systemie. Domyślnie external-api. |
cv | nie | file | 5 MB | CV kandydata. Format: PDF, DOC/DOCX, ODT, RTF, TXT, JPG, PNG. Plik skanowany antywirusowo. |
Akceptowane typy MIME pliku CV: application/pdf, application/x-pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.oasis.opendocument.text, application/vnd.oasis.opendocument.text-master, application/rtf, text/plain, image/jpeg, image/png.
Odpowiedź 201:
{
"candidateId": "4b257ea8-5eca-46da-8a26-562c6d78bd2d",
"projectId": "ff315ea1-c056-472d-aefe-b99f3d41e5c0"
}
Przykład:
curl --user "api+voicebot@acme.local:<haslo>" \
-F "firstName=Anna" \
-F "lastName=Nowak" \
-F "email=anna.nowak@example.com" \
-F "phoneNumber=+48500111222" \
-F "source=voicebot-inbound" \
-F "cv=@./cv.pdf" \
https://acme.elementapp.ai/api/v1-beta/projects/ff315ea1-c056-472d-aefe-b99f3d41e5c0/apply
Możliwe błędy: 400 validation_failed, 401, 404 project_not_found, 409 no_application_stage, 429.
8.12. Dodanie notatki
Dodaje publiczną notatkę do profilu kandydata. Notatki dodane przez API są zawsze publiczne. Autorem jest Twoje konto API — w UI Element widoczne jako API: <nazwa-systemu>.
Content-Type: application/x-www-form-urlencoded.
Pola requestu:
| Pole | Wymagane | Typ | Limit | Opis |
|---|---|---|---|---|
content | tak | string | 10 000 znaków | Treść notatki (plain text). |
projectId | nie | UUID | — | Powiąż notatkę z konkretnym projektem. Bez projectId notatka jest na ogólnym profilu kandydata. |
Odpowiedź 201:
{ "noteId": "898a1822-ac9e-4473-9f56-e0a8542676e7" }
Przykład:
curl --user "api+voicebot@acme.local:<haslo>" \
--data-urlencode "content=Voice bot — kandydat oddzwoni jutro między 10:00 a 12:00." \
--data-urlencode "projectId=ff315ea1-c056-472d-aefe-b99f3d41e5c0" \
https://acme.elementapp.ai/api/v1-beta/candidates/4b257ea8-5eca-46da-8a26-562c6d78bd2d/notes
Możliwe błędy: 400 validation_failed, 401, 404 candidate_not_found, 429.
9. Scenariusze integracji
9.1. Voice bot — przyjęcie aplikacji telefonicznej
Voice bot dzwoni do kandydata, zbiera dane, zapisuje aplikację i notatkę z rozmowy.
BASE="https://acme.elementapp.ai"
AUTH="api+voicebot@acme.local:<haslo>"
PROJECT_ID="ff315ea1-c056-472d-aefe-b99f3d41e5c0"
# 1. (Opcjonalnie) sprawdź wymagane pola formularza
curl --user "$AUTH" "$BASE/api/v1-beta/projects/$PROJECT_ID/application-form"
# 2. Złóż aplikację
RESP=$(curl --user "$AUTH" \
-F "firstName=Anna" \
-F "lastName=Nowak" \
-F "email=anna.nowak@example.com" \
-F "phoneNumber=+48500111222" \
-F "source=voicebot" \
"$BASE/api/v1-beta/projects/$PROJECT_ID/apply")
CANDIDATE_ID=$(echo "$RESP" | jq -r .candidateId)
# 3. Zapisz transkrypcję najważniejszych ustaleń jako notatkę
curl --user "$AUTH" \
--data-urlencode "content=Voice bot — kandydat potwierdził dostępność od 1 czerwca, oczekiwania 15k netto, otwarty na pracę zdalną." \
--data-urlencode "projectId=$PROJECT_ID" \
"$BASE/api/v1-beta/candidates/$CANDIDATE_ID/notes"
9.2. System ATS — synchronizacja statusów kandydatów
System ATS partnera regularnie pobiera status kandydatów w projektach Element, aby zachować spójność widoków.
# Pobierz aktualne projekty
PROJECTS=$(curl --user "$AUTH" "$BASE/api/v1-beta/projects?perPage=100")
# Dla każdego projektu, pobierz kandydatów wraz ze stage
for PROJECT_ID in $(echo "$PROJECTS" | jq -r '.data[].id'); do
curl --user "$AUTH" "$BASE/api/v1-beta/projects/$PROJECT_ID/candidates?perPage=100"
sleep 1 # backoff — szanuj limit 70/min
done
9.3. Pipeline danych — eksport odpowiedzi z formularzy
System analityczny pobiera odpowiedzi kandydatów na pytania formularza dla projektu.
PROJECT_ID="ff315ea1-c056-472d-aefe-b99f3d41e5c0"
# 1. Lista kandydatów w projekcie
CANDIDATES=$(curl --user "$AUTH" "$BASE/api/v1-beta/projects/$PROJECT_ID/candidates?perPage=100")
# 2. Dla każdego — pobierz wypełniony formularz
for CANDIDATE_ID in $(echo "$CANDIDATES" | jq -r '.data[].id'); do
curl --user "$AUTH" "$BASE/api/v1-beta/projects/$PROJECT_ID/candidates/$CANDIDATE_ID/application-form?locale=pl"
sleep 1
done
10. Bezpieczeństwo i higiena integracji
10.1. Higiena credentials
- Jeden zestaw credentials per system zewnętrzny. Łatwiejszy audyt; w razie kompromitacji jednego systemu pozostałe nie tracą dostępu.
- Zapisuj hasło w sejfie sekretów. Nigdy w kodzie, w plikach konfiguracyjnych pod kontrolą wersji, w wiadomościach komunikatorów ani w opisach issue'ów.
- Rotuj okresowo. Przycisk Regeneruj hasło w panelu pozwala na ciche podmienienie hasła bez utraty audit trail wcześniejszych wywołań.
- Natychmiast unieważniaj po wycieku. Stare hasło przestaje działać od razu po regeneracji nowego.
10.2. Idempotencja po stronie integratora
API nie deduplikuje wywołań. Retry sieciowy po POST /apply utworzy drugiego kandydata. Stosuj jeden z mechanizmów po Twojej stronie:
- Sprawdź przed: zapytaj
/projects/{id}/candidates, czy kandydat z danym emailem już istnieje, zanim wyśleszPOST /apply. - Sprawdź po: zapisz
candidateIdzwrócony przez/applyzanim oznaczysz operację jako wykonaną. Jeśli retry — sprawdź czy istnieje ten konkretny kandydat zanim ponowisz. - Outbox pattern: stwórz lokalny outbox z idempotency-key (np. transcript hash), oznaczaj „sent" dopiero po sukcesie.
10.3. Backoff i rate limit
- Stosuj exponential backoff od
1s, podwajaj do30sna 429. - Rozkładaj batch'e w czasie zamiast paralelizować — limit 70/min to ~1.2 req/s sustained.
- Zaplanuj swoje cykle synchronizacji tak, żeby nie nakładały się na siebie z innymi integracjami tego samego credential.
10.4. Logowanie po stronie integratora
Loguj zawsze:
- request URL i metodę,
- HTTP status odpowiedzi,
- timestamp wywołania,
- własne ID korelacji (
candidateId,noteId, batch-id), - pierwsze ~256 znaków body odpowiedzi w razie błędu.
Te dane wraz z timestampem i loginem API pozwolą supportowi Element zlokalizować dokładnie ten request w audit logu po stronie Element w razie problemu.
10.5. Dane wrażliwe
- Dane kandydatów to dane osobowe — obowiązują Cię standardowe zasady RODO/GDPR po stronie Twojego systemu.
- Element zwraca
404 candidate_not_founddla kandydatów, którzy zażądali usunięcia danych. Twój system powinien wtedy również usunąć kopię tych danych. - Nie używaj danych kandydatów do celów innych niż uzgodniony z tenantem zakres integracji.
11. Słownik pojęć
| Pojęcie | Znaczenie |
|---|---|
| Tenant | Klient Element (np. firma rekrutacyjna). Identyfikowany subdomeną (acme.elementapp.ai). Credentials API są zlocked do jednego tenanta. |
| Projekt | Proces rekrutacyjny na jedno stanowisko. Posiada listę etapów. |
| Etap (stage) | Krok w procesie rekrutacyjnym (np. „Aplikacja", „Rozmowa", „Ofertowanie"). |
| Etap typu „aplikacja" | isApplicationType: true — etap, w którym lądują nowo zaaplikowani kandydaci. POST /apply zawsze przypisuje nowych kandydatów do tego etapu. Każdy projekt ma co najwyżej jeden taki etap. |
| Kandydat | Osoba zarejestrowana w CRM Element. Może uczestniczyć w wielu projektach jednocześnie. |
| Notatka publiczna | Notatka widoczna dla wszystkich konsultantów tenanta. API zawsze tworzy notatki publiczne. |
| Notatka prywatna | Notatka widoczna tylko dla autora. Niedostępna przez API. |
| Formularz aplikacyjny | Konfigurowalny zestaw pytań do kandydatów. v1-beta udostępnia metadane formularza projektu oraz odpowiedzi już złożone przez kandydatów. |
| RODO / forgotten | Kandydat, który zażądał usunięcia danych osobowych. Zwraca 404 candidate_not_found. |
12. Zakres v1-beta i plan rozwoju
12.1. Co jest dostępne w v1-beta
Odczyt (GET): projekty, etapy, kandydaci w projekcie, metadane formularza, wypełniony formularz kandydata, profil kandydata, projekty kandydata, publiczne notatki kandydata.
Zapis (POST): złożenie aplikacji w projekcie (utworzenie kandydata), dodanie publicznej notatki do kandydata.
12.2. Planowane rozszerzenia (poza v1-beta)
Po ustabilizowaniu kontraktu v1-beta i przejściu na v1 planowane jest rozszerzenie API o dodatkowe operacje workflow rekrutacyjnego: zmiana etapu kandydata, odrzucenie kandydata, zmiana oceny, dodanie istniejącego kandydata do projektu, utworzenie projektu.
Zakres i terminy będą komunikowane oddzielnie.
12.3. Świadomie pominięte
- Pełna struktura pytań formularza aplikacyjnego — integratorzy używają własnego UI do zbierania danych, nie renderują formularzy Element.
- Pole
answerswPOST /apply— aplikacja przez API zawiera tylko dane kontaktowe i CV. - Notatki prywatne — nie są zwracane ani tworzone przez API.
- Wewnętrzny opis kandydata w CRM Element (
description). - Wewnętrzny scoring kandydata w formularzu (
obtainedScore,isDisqualified).
12.4. Polityka zmian w okresie beta
- Kontrakt API może podlegać niekompatybilnym zmianom w okresie beta (~2–3 miesiące produkcyjnego użycia).
- Istotne zmiany będą sygnalizowane z wyprzedzeniem przez zespół Element.
- Po zakończeniu okresu beta wersja
v1-betazostanie zamrożona jako stabilnev1. Wszelkie kolejne breaking changes pojawią się pod nowym prefiksem (/api/v2/, …). - Drobne uzupełnienia kontraktu (nowe opcjonalne pola w odpowiedziach, nowe endpointy, nowe wartości
error.code) nie są traktowane jako breaking i mogą pojawiać się również w okresie beta — Twój klient HTTP powinien tolerować dodatkowe pola.
13. Kontakt i wsparcie
W razie problemów technicznych z API:
- Skontaktuj się ze standardowym wsparciem Element przez kanał uzgodniony z Twoim opiekunem kontraktu.
- W zgłoszeniu podaj:
- subdomenę tenanta (
acme.elementapp.ai), - login API (
api+<system>@<tenant>.local), - timestamp problematycznego wywołania (UTC),
- HTTP method + ścieżkę (np.
POST /api/v1-beta/projects/.../apply), - HTTP status otrzymanej odpowiedzi,
- body odpowiedzi (przynajmniej
error.codeierror.message), - Twój request ID lub inny identyfikator korelacji, jeśli logujesz po swojej stronie.
- subdomenę tenanta (
Te dane pozwolą zlokalizować konkretny request w audit logu Element i przyspieszyć diagnozę.