Conectează orice magazin în câteva ore
Un API simplu, în loturi de până la 500 de obiecte, un script pentru site, webhooks semnate și un mediu de test în care nimic nu se salvează.
Primii pași
API-ul primește datele magazinului tău (clienți, produse, comenzi, evenimente) din orice platformă. Shopify și WooCommerce sunt conectate automat; restul folosesc API-ul de mai jos.
- Creează o cheie în Setări → General → Chei API. Pentru probe, alege „Creează cheie de test”.
- Verifică cheia cu GET /ping.
- Trimite întâi categoriile și produsele, apoi clienții, apoi comenzile (comenzile se leagă de clienți și produse prin external_id).
- Pune scriptul pe site pentru evenimentele de navigare (vizualizări, coș, căutări).
curl https://marketing.conekto.app/api/v1/ping \
-H "Authorization: Bearer ck_live_…"{
"ok": true,
"test": false,
"shop": {
"name": "Magazinul Meu",
"domain": "magazin.ro",
"platform": "custom",
"public_key": "pk_…"
}
}Autentificare și limite
Fiecare cerere are antetul Authorization cu cheia magazinului. Cheia identifică magazinul, deci nu trimiți niciodată un id de magazin.
| Regulă | Valoare |
|---|---|
| Antet | Authorization: Bearer ck_live_… (sau ck_test_…) |
| Format | JSON, Content-Type: application/json, UTF-8 |
| Limită de cereri | 600 pe minut pe cheie (în medie 10 pe secundă) |
| Mărimea unui lot | între 1 și 500 de obiecte într-o cerere |
| Sume | număr sau text cu punct zecimal: 349, "349.00"; se rotunjesc la 2 zecimale |
| Date | ISO 8601 cu fus orar: 2026-11-03T20:15:00+02:00 |
Răspunsuri și erori
Un lot nu se oprește la primul obiect greșit: obiectele bune se salvează, iar cele greșite apar în errors, cu poziția lor în listă.
{
"received": 2,
"created": 1,
"updated": 0,
"errors": [
{
"index": 1,
"message": "email: Contactul are nevoie de email sau telefon."
}
]
}| Cod | Când | Ce faci |
|---|---|---|
| 200 | Lotul a fost procesat (poate avea erori pe obiecte) | Verifică errors; retrimite doar obiectele de la acele poziții, corectate |
| 400 | Body-ul nu are forma { <listă>: [...] } sau are peste 500 de obiecte | Corectează forma cererii |
| 401 | Cheia lipsește, e greșită sau a fost retrasă | Creează o cheie nouă din Setări |
| 403 | Magazinul nu mai e activ | Scrie-ne la suport |
| 405 | Altă metodă decât POST (sau GET la /ping) | Folosește metoda corectă |
| 429 | Peste 600 de cereri pe minut | Așteaptă câte secunde spune antetul Retry-After, apoi reia |
| 5xx | Eroare la noi | Reîncearcă după câteva secunde, cu pauze tot mai mari |
Retrimiterea aceluiași obiect e sigură: totul se salvează după external_id, deci a doua oară obiectul doar se actualizează (updated crește, nu created).
Contacte
POST /contacts creează sau actualizează clienți după external_id (id-ul din platforma ta). Fiecare contact are nevoie de email sau telefon.
{
"contacts": [
{
"external_id": "1042",
"email": "ana.pop@exemplu.ro",
"phone": "0722123456",
"first_name": "Ana",
"last_name": "Pop",
"city": "Cluj-Napoca",
"county": "Cluj",
"country": "RO",
"email_consent": {
"state": "subscribed",
"updated_at": "2026-10-02T09:14:00+03:00"
},
"sms_consent": {
"state": "not_subscribed"
},
"orders_count": 3,
"total_spent": "742.50",
"last_order_at": "2026-10-28T18:40:00+02:00",
"created_at": "2025-03-11T10:00:00+02:00"
}
]
}| Câmp | Tip | Descriere |
|---|---|---|
| external_id | text, obligatoriu | Id-ul clientului în platforma ta |
| email / phone | text | Cel puțin unul. Telefonul se normalizează la formatul internațional (+40…) |
| first_name, last_name, city, county | text | Folosite în personalizare și segmente |
| country | 2 litere | RO, MD… |
| email_consent / sms_consent | obiect | state: subscribed, not_subscribed, unsubscribed sau pending; updated_at opțional |
| orders_count, total_spent, last_order_at | număr / sumă / dată | Istoricul, dacă nu trimiți comenzile; altfel se calculează din comenzi |
| created_at | dată | Când și-a făcut clientul cont |
Categorii
POST /categories. Trimite-le înaintea produselor; ierarhia se face cu parent_external_id.
{
"categories": [
{
"external_id": "12",
"name": "Încălțăminte",
"url": "https://magazin.ro/incaltaminte"
},
{
"external_id": "31",
"name": "Sneakers",
"url": "https://magazin.ro/incaltaminte/sneakers",
"parent_external_id": "12"
}
]
}Produse
POST /products. Produsele apar în recomandări, în blocurile de produse din emailuri și în fluxurile de stoc și preț.
{
"products": [
{
"external_id": "4421",
"title": "Sneakers Urban Alb",
"url": "https://magazin.ro/sneakers-urban-alb",
"image_url": "https://magazin.ro/img/4421.jpg",
"brand": "Urban",
"categories": [
"31"
],
"tags": [
"nou",
"vara"
],
"status": "active",
"variants": [
{
"external_id": "4421-40",
"sku": "URB-40",
"title": "40",
"price": "349.00",
"compare_at_price": "399.00",
"in_stock": true,
"stock_qty": 6
},
{
"external_id": "4421-41",
"sku": "URB-41",
"title": "41",
"price": "349.00",
"in_stock": false,
"stock_qty": 0
}
]
},
{
"external_id": "88",
"title": "Cutie cadou",
"price": 19.9,
"in_stock": true
}
]
}categoriesare external_id-urile categoriilor trimise la /categories.variants: până la 250. Un produs fără variante, dar cupricesauin_stock, primește automat o variantă<external_id>#default.compare_at_pricemai mare decâtpriceînseamnă reducere. Scăderea prețului pornește fluxul „Preț redus” pentru cei care au văzut produsul sau l-au pus la favorite în ultimele 30 de zile.- Trecerea
in_stockdin false în true pornește fluxul „Înapoi în stoc”, pentru aceiași vizitatori. status: active, draft sau archived. Doar produsele active apar în recomandări.
Comenzi
POST /orders. Comenzile calculează venitul atribuit campaniilor și fluxurilor, RFM-ul și segmentele după cumpărături.
{
"orders": [
{
"external_id": "98123",
"number": "#10045",
"customer_external_id": "1042",
"email": "ana.pop@exemplu.ro",
"currency": "RON",
"discount_total": "34.90",
"shipping_total": "19.99",
"total": "334.09",
"financial_status": "paid",
"fulfillment_status": "unfulfilled",
"discount_codes": [
"BINEAIVENIT10"
],
"source_name": "web",
"placed_at": "2026-11-03T20:15:00+02:00",
"lines": [
{
"product_external_id": "4421",
"variant_external_id": "4421-40",
"sku": "URB-40",
"title": "Sneakers Urban Alb",
"variant_title": "40",
"quantity": 1,
"price": "349.00"
}
]
}
]
}| Câmp | Valori |
|---|---|
| financial_status | pending, paid, partially_paid, partially_refunded, refunded, voided |
| fulfillment_status | unfulfilled, partial, fulfilled |
| subtotal | Opțional: dacă lipsește, e suma liniilor (preț × cantitate) |
| refunded_total, cancelled_at | Trimite din nou comanda când se returnează sau se anulează; venitul se corectează |
Comanda se leagă de client după customer_external_id, apoi după email, apoi după telefon. O comandă nouă scoate clientul din fluxurile care se opresc la comandă (de exemplu coș abandonat și produse vizitate).
Evenimente de pe server
POST /events, pentru ce se întâmplă pe server (de exemplu hook-uri PHP). Pentru navigarea de pe site folosește scriptul; e mai simplu și leagă singur vizitatorul de client.
{
"events": [
{
"type": "view_product",
"email": "ana.pop@exemplu.ro",
"product_external_id": "4421",
"url": "https://magazin.ro/sneakers-urban-alb"
},
{
"type": "add_to_cart",
"contact_external_id": "1042",
"product_external_id": "4421",
"value": "349.00",
"props": {
"quantity": 1
}
},
{
"type": "search",
"vid": "v_8f2a91c0d1",
"props": {
"query": "sneakers albi"
},
"at": "2026-11-04T10:02:00+02:00"
}
]
}| Câmp | Descriere |
|---|---|
| type | page_view, view_product, view_category, search, add_to_cart, remove_from_cart, add_to_wishlist, remove_from_wishlist, initiate_checkout, checkout_completed, order, identify |
| vid / contact_external_id / email / phone | Cel puțin unul, ca să știm al cui e evenimentul. vid e cookie-ul ckm_vid de pe site |
| product_external_id, value, url | Opționale |
| props | Obiect liber, cel mult 4 KB (de exemplu query, quantity, cart). props.sid leagă evenimentul de sesiunea de pe site |
| at | Momentul evenimentului; dacă lipsește, acum |
Ștergeri
POST /contacts/delete, /products/delete și /orders/delete primesc o listă de cel mult 500 de external_id. Fiecare ștergere rămâne în jurnalul contului.
{
"external_ids": [
"1042"
],
"reason": "gdpr"
}{
"deleted": 1
}Contactul se șterge cu tot istoricul lui. Comenzile rămân în rapoarte, fără date personale. Cu reason „gdpr” (sau „erasure”, „forget”), emailul și telefonul intră și pe lista de excludere: nu mai primesc nimic, nici dacă revin în listă. Fără motiv (de exemplu client șters din magazin), aceeași persoană se poate abona din nou.
Scriptul de pe site
Un singur script pentru evenimente, formulare, pop-up-uri și notificări push. Shopify și WooCommerce îl pun singure; pe alt site îl copiezi din Setări → General.
<script>window.ckmSettings = { k: "pk_…" };</script>
<script async src="https://marketing.conekto.app/t/s.js?k=pk_…"></script>Scriptul numără singur paginile văzute și sesiunile. Restul evenimentelor le trimiți cu funcția ckm:
ckm("track", "view_product", { product_id: "4421" });
ckm("track", "add_to_cart", { product_id: "4421", props: { cart: { value: 349, items: 1, l: [{ p: "4421", q: 1, v: "4421-40" }] } } });
ckm("track", "search", { props: { query: "sneakers" } });
ckm("identify", { email: "ana.pop@exemplu.ro", src: "form" });| Setare în ckmSettings | Efect |
|---|---|
consent: false | Nu trimite nimic până nu schimbi în true (pentru bannerul de cookie-uri) |
cart: {...} | Coșul curent, în formatul de mai sus, pe orice pagină |
dataLayer: false | Oprește preluarea evenimentelor vechi din dataLayer |
Evenimente vechi din dataLayer
Dacă site-ul trimite deja evenimente __sm__* în dataLayer (de la platforma de marketing folosită înainte), scriptul nostru le preia singur. Nu trebuie schimbat nimic pe site.
| Eveniment în dataLayer | Devine | Observații |
|---|---|---|
__sm__set_email | identify | emailul scris pe site; nu e acord de marketing |
__sm__view_homepage | page_view | pagina se numără oricum automat |
__sm__view_category | view_category | calea categoriei, ca în feed („A|B|C”) |
__sm__view_brand | page_view | pagina de brand |
__sm__view_product | view_product | id-ul produsului din feed |
__sm__add_to_cart | add_to_cart | produs, cantitate, variantă |
__sm__remove_from_cart | remove_from_cart | produs, cantitate, variantă |
__sm__add_to_wishlist | add_to_wishlist | produs, variantă |
__sm__remove_from_wishlist | remove_from_wishlist | produs, variantă |
__sm__initiate_checkout | initiate_checkout | intrarea în checkout |
__sm__search | search | termenul căutat |
__sm__order | identify | comanda vine din platformă, API sau feed; de pe pagină luăm doar emailul |
Vezi și pagina Setări → Mutare de pe altă platformă din aplicație: acolo urmărești pas cu pas ce date au ajuns.
Feed de produse și de comenzi
Pentru mutare sau pentru platformele fără API: un feed XML de produse, citit zilnic, și un feed JSON de comenzi, citit pe pagini. Le configurezi în Setări → Mutare.
<products>
<product>
<id>4421</id>
<sku>URB</sku>
<name><![CDATA[Sneakers Urban Alb]]></name>
<url>https://magazin.ro/sneakers-urban-alb</url>
<main_image>https://magazin.ro/img/4421.jpg</main_image>
<category><![CDATA[Încălțăminte|Sneakers]]></category>
<brand>Urban</brand>
<price>399</price>
<sale_price>349</sale_price>
<availability>1</availability>
<stock>6</stock>
<variations>
<variation><id>4421-40</id><sku>URB-40</sku><price>399</price><sale_price>349</sale_price><size>40</size><availability>1</availability><stock>6</stock></variation>
</variations>
</product>
</products>- Categoria are nivelurile despărțite de „|”; fiecare nivel devine o categorie.
- availability: 0 = stoc epuizat, 1 sau 2 = în stoc.
- sale_price contează doar între sale_price_start_date și sale_price_end_date, dacă sunt date (ora României).
- Sumele pot fi „1.234,50” sau „1234.50”.
{
"orders": {
"order": [
{
"order_no": "10045",
"order_status": "Livrată",
"created_at": "2026-11-03 20:15",
"email_address": "ana.pop@exemplu.ro",
"phone": "0722123456",
"firstname": "Ana",
"lastname": "Pop",
"discount_code": "BINEAIVENIT10",
"discount_value": "34.90",
"shipping": "19.99",
"total_value": "334.09",
"refund_value": "0",
"products": [
{
"product_id": "4421",
"name": "Sneakers Urban Alb",
"variation_sku": "URB-40",
"price": "399",
"sale_price": "349",
"quantity": 1
}
]
}
]
}
}Citim ?page=1, 2, 3… până la o pagină goală. Sunt acceptate și forma { orders: [...] } și o listă simplă.
Webhooks
Primești pe serverul tău, în timp real, ce se întâmplă în cont. Le adaugi în Setări → Webhooks, unde vezi și ultimele livrări și poți trimite un test.
| Eveniment | Când |
|---|---|
contact.subscribed | Un contact s-a abonat la email sau SMS (formular, pop-up, confirmare). |
contact.unsubscribed | Un contact s-a dezabonat sau adresa a fost curățată. |
form.submitted | Un formular sau pop-up a fost trimis, cu câmpurile completate. |
campaign.sent | O campanie a terminat trimiterea, cu numărul de destinatari. |
transactional.sent | Un email sau SMS din API ori SMTP a plecat la furnizor. |
transactional.delivered | Furnizorul confirmă că mesajul a ajuns la destinatar. |
transactional.bounced | Adresa sau numărul a respins mesajul (bounce). |
transactional.complained | Destinatarul a marcat emailul ca spam. |
transactional.failed | Mesajul nu a plecat: adresă exclusă, mod de test, credit sau eroare. |
{
"id": "whd_…",
"event": "contact.subscribed",
"createdAt": "2026-11-04T08:12:31.000Z",
"shop": "magazin.ro",
"data": {
"id": "…",
"email": "ana.pop@exemplu.ro",
"emailStatus": "SUBSCRIBED",
"smsStatus": "NOT_SUBSCRIBED",
"whatsappStatus": "NOT_SUBSCRIBED",
"tags": [],
"source": "form"
}
}| Antet | Conținut |
|---|---|
| X-Conekto-Event | Numele evenimentului |
| X-Conekto-Delivery | Id-ul livrării; îl folosești ca să nu procesezi de două ori |
| X-Conekto-Timestamp | Secunde de la 1970 |
| X-Conekto-Signature | sha256= + HMAC SHA-256 cu secretul webhook-ului, peste <timestamp>.<corp> |
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_CONEKTO_TIMESTAMP'];
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, getenv('CONEKTO_WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_X_CONEKTO_SIGNATURE']) || abs(time() - (int)$ts) > 300) {
http_response_code(401); exit;
}- Răspunde cu un cod 2xx în cel mult 10 secunde; altfel livrarea se reia de până la 5 ori, la intervale tot mai mari.
- După 50 de eșecuri la rând, webhook-ul se oprește singur și îl repornești din Setări.
- Doar adrese https publice; istoricul livrărilor se păstrează 30 de zile.
Mediu de test
Faci integrarea fără ca datele de probă să ajungă în contacte, comenzi sau rapoarte.
- Cheile
ck_test_…răspund exact ca cele reale (aceleași validări, aceleași erori pe obiecte), dar nu salvează și nu șterg nimic. Răspunsul are"test": true. - Domeniile de test (Setări → Mediu de test): scriptul pus pe staging trimite evenimente care nu se salvează și nu pornesc automatizări.
- Ce primim de la cheile și domeniile de test apare în jurnalul din aceeași pagină, în câteva secunde (ultimele 100 de cereri, 24 de ore).
curl -X POST https://marketing.conekto.app/api/v1/contacts \
-H "Authorization: Bearer ck_test_…" \
-H "Content-Type: application/json" \
-d '{"contacts":[{"external_id":"42","email":"ion@exemplu.ro","email_consent":{"state":"subscribed"}}]}'{
"received": 1,
"created": 1,
"updated": 0,
"errors": [],
"test": true,
"note": "Cheie de test: datele au fost verificate, dar nu au fost salvate."
}Documentația pentru asistenți AI
Toată documentația, ca un singur fișier text, pentru Claude, ChatGPT sau Cursor.
/llms.txt: rezumatul și legăturile, după standardul llms.txt./dezvoltatori.md: toată documentația într-un singur fișier Markdown.- Conectorul Claude (MCP) din aplicație: Claude lucrează direct în cont (campanii, segmente, rapoarte).
Ce s-a schimbat
Schimbările din API, script și webhooks. Orice schimbare care cere modificări în integrarea ta apare aici înainte s-o facem.
- Mediu de test și mutare de pe altă platformă
- Chei API de test (ck_test_…): aceleași validări, nimic salvat; răspunsul are „test”: true.
- Domenii de test pentru script: evenimentele de pe staging nu se salvează și apar în jurnalul de test.
- Scriptul preia evenimentele vechi din dataLayer (__sm__*), fără schimbări pe site.
- Import din feed XML de produse (zilnic) și feed JSON de comenzi, pe pagini.
- Webhooks
- 4 evenimente: abonare, dezabonare, formular trimis, campanie trimisă. Semnătură HMAC SHA-256, 5 reîncercări.
- API v1
- Contacte, produse, categorii, comenzi, evenimente și ștergeri, în loturi de până la 500 de obiecte.
Scrie-ne cu exemplul de cerere și răspunsul primit. Te ajutăm să găsești problema.