Přeskočit na obsah
API dokumentace

Recept: import kontaktů z CSV

Cíl: nahrát kontakty z CSV souboru se souhlasem, který máte doložený, a ověřit výsledek.

Potřebujete: klíč s modulem contacts na úrovni full.

1. Nahrání souboru

Dvě možnosti:

  • multipart/form-data s polem file (soubor do 64 MB a 300 000 řádků, kódování UTF-8, UTF-16 nebo windows-1250 se pozná samo) a volitelně delimiter, quote, hasHeader, encoding.
  • JSON {"text": "...", "delimiter", "quote", "hasHeader"} s obsahem do 5 MB.
Požadavek: POST /api/v1/tenants/{tenant}/imports/files
curl -sS -X POST "https://www.postarna.cz/api/v1/tenants/$TENANT/imports/files" \
  -H "Authorization: Bearer $POSTARNA_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "text": "email;jmeno;prijmeni;mesto\nmarie.dvorakova@example.cz;Marie;Dvořáková;Praha\ntomas.cerny@example.cz;Tomáš;Černý;Ostrava\nchybna-adresa;Jan;Bez;Plzeň\n",
  "delimiter": ";",
  "quote": "\"",
  "hasHeader": true
}
JSON
Odpověď: 201 Created
{
  "fileId": "71",
  "fileName": "vlozeny-text.csv",
  "size": 155,
  "delimiter": ";",
  "quote": "\"",
  "hasHeader": true,
  "encoding": "utf-8",
  "columns": [
    "email",
    "jmeno",
    "prijmeni",
    "mesto"
  ],
  "rowCount": 3,
  "sample": [
    [
      "marie.dvorakova@example.cz",
      "Marie",
      "Dvořáková",
      "Praha"
    ],
    [
      "tomas.cerny@example.cz",
      "Tomáš",
      "Černý",
      "Ostrava"
    ],
    [
      "chybna-adresa",
      "Jan",
      "Bez",
      "Plzeň"
    ]
  ],
  "suggestedKind": "csv",
  "suggestedMapping": [
    "email",
    "firstName",
    "lastName",
    "city"
  ]
}

Odpověď má fileId, rozpoznané sloupce, ukázku řádků a suggestedMapping (návrh, které pole je ve kterém sloupci). Soubor se drží 24 hodin.

2. Náhled

mapping má jednu položku na sloupec souboru, v pořadí: název pole, nebo null (sloupec přeskočit). Právě jeden sloupec musí být email. Pole: email, firstName, lastName, fullName, titleBefore, titleAfter, phone, mobile, company, companyId, street, city, postalCode, country, language, gender, birthday, note, newsletterConsent, newsletterConsentAt, registeredAt a vlastní pole.

Požadavek: POST /api/v1/tenants/{tenant}/imports/preview
curl -sS -X POST "https://www.postarna.cz/api/v1/tenants/$TENANT/imports/preview" \
  -H "Authorization: Bearer $POSTARNA_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "fileId": "71",
  "mapping": [
    "email",
    "firstName",
    "lastName",
    "city"
  ],
  "mode": "upsert",
  "shopId": "9",
  "listIds": []
}
JSON
Odpověď: 200 OK
{
  "counts": {
    "rows": 3,
    "create": 2,
    "update": 0,
    "skipSuppressed": 0,
    "skipMissing": 0,
    "invalid": 1,
    "duplicate": 0,
    "unsubscribe": 0,
    "remove": 0,
    "blacklist": 0
  },
  "rows": [
    {
      "row": 2,
      "email": "marie.dvorakova@example.cz",
      "action": "create",
      "error": null,
      "field": null,
      "values": {
        "email": "marie.dvorakova@example.cz",
        "firstName": "Marie",
        "lastName": "Dvořáková",
        "city": "Praha"
      },
      "consent": true
    },
    {
      "row": 3,
      "email": "tomas.cerny@example.cz",
      "action": "create",
      "error": null,
      "field": null,
      "values": {
        "email": "tomas.cerny@example.cz",
        "firstName": "Tomáš",
        "lastName": "Černý",
        "city": "Ostrava"
      },
      "consent": true
    },
    {
      "row": 4,
      "email": "chybna-adresa",
      "action": "invalid",
      "error": "email_invalid",
      "field": "email",
      "values": {
        "email": "chybna-adresa",
        "firstName": "Jan",
        "lastName": "Bez",
        "city": "Plzeň"
      },
      "consent": null
    }
  ],
  "report": {
    "consent": {
      "optIn": 2,
      "none": 0,
      "keptUnsubscribed": 0
    },
    "merge": {
      "existing": 0,
      "fromSources": {},
      "fieldsUpdated": 0,
      "fieldsKeptEdited": 0
    },
    "dropped": {}
  }
}

counts říká, kolik řádků se založí, aktualizuje, přeskočí (potlačené adresy, chyby) a proč; rows ukazuje první řádky s jejich výsledkem.

3. Spuštění

  • mode: upsert (založit i aktualizovat), update_only, nebo hromadné odhlášení unsubscribe, odebrání remove, blacklist blacklist.
  • purposeId a consentConfirmed: true: potvrzujete, že ke všem kontaktům máte doložený souhlas s tímto účelem (u unsubscribe, remove a blacklist souhlas potřeba není).
  • listIds: ruční seznamy, do kterých kontakty přibudou.
  • runAutomations: výchozí false = import nespustí žádnou automatizaci (ani „nový kontakt“ či „přidání do seznamu“). Zapněte jen vědomě.
  • kind: csv, nebo exporty Shoptetu shoptet_customers a shoptet_newsletter.
Požadavek: POST /api/v1/tenants/{tenant}/imports
curl -sS -X POST "https://www.postarna.cz/api/v1/tenants/$TENANT/imports" \
  -H "Authorization: Bearer $POSTARNA_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "name": "Kontakty z veletrhu",
  "source": "csv",
  "fileId": "71",
  "mapping": [
    "email",
    "firstName",
    "lastName",
    "city"
  ],
  "mode": "upsert",
  "shopId": "9",
  "listIds": [],
  "purposeId": "5",
  "consentConfirmed": true
}
JSON
Odpověď: 201 Created
{
  "id": "61",
  "name": "Kontakty z veletrhu",
  "status": "finished",
  "kind": "csv",
  "fileName": "vlozeny-text.csv",
  "shopId": "9",
  "mode": "upsert",
  "listIds": [],
  "createdAt": "2026-10-01T08:00:00.000Z",
  "startedAt": "2026-10-01T08:00:00.000Z",
  "finishedAt": "2026-10-01T08:00:00.000Z",
  "progress": {
    "processed": 3,
    "total": 3
  },
  "counts": {
    "rows": 3,
    "create": 2,
    "remove": 0,
    "update": 0,
    "invalid": 1,
    "blacklist": 0,
    "duplicate": 0,
    "skipMissing": 0,
    "unsubscribe": 0,
    "skipSuppressed": 0
  },
  "source": "csv",
  "purposeId": "5",
  "addedToLists": 0,
  "errors": [
    {
      "row": 4,
      "error": "email_invalid",
      "field": "email"
    }
  ],
  "runAutomations": false,
  "options": {},
  "report": {
    "consent": {
      "none": 0,
      "optIn": 2,
      "keptUnsubscribed": 0
    },
    "merge": {
      "existing": 0,
      "fromSources": {},
      "fieldsUpdated": 0,
      "fieldsKeptEdited": 0
    },
    "dropped": {}
  }
}

Do 1 000 řádků proběhne import hned (status: "finished"); větší jde do fronty (queued, running s průběhem progress) a dokončí ho worker.

4. Výsledek

Požadavek: GET /api/v1/tenants/{tenant}/imports/61
curl -sS -X GET "https://www.postarna.cz/api/v1/tenants/$TENANT/imports/61" \
  -H "Authorization: Bearer $POSTARNA_API_KEY"
Odpověď: 200 OK
{
  "id": "61",
  "name": "Kontakty z veletrhu",
  "status": "finished",
  "kind": "csv",
  "fileName": "vlozeny-text.csv",
  "shopId": "9",
  "mode": "upsert",
  "listIds": [],
  "createdAt": "2026-10-01T08:00:00.000Z",
  "startedAt": "2026-10-01T08:00:00.000Z",
  "finishedAt": "2026-10-01T08:00:00.000Z",
  "progress": {
    "processed": 3,
    "total": 3
  },
  "counts": {
    "rows": 3,
    "create": 2,
    "remove": 0,
    "update": 0,
    "invalid": 1,
    "blacklist": 0,
    "duplicate": 0,
    "skipMissing": 0,
    "unsubscribe": 0,
    "skipSuppressed": 0
  },
  "source": "csv",
  "purposeId": "5",
  "addedToLists": 0,
  "errors": [
    {
      "row": 4,
      "error": "email_invalid",
      "field": "email"
    }
  ],
  "runAutomations": false,
  "options": {},
  "report": {
    "consent": {
      "none": 0,
      "optIn": 2,
      "keptUnsubscribed": 0
    },
    "merge": {
      "existing": 0,
      "fromSources": {},
      "fieldsUpdated": 0,
      "fieldsKeptEdited": 0
    },
    "dropped": {}
  }
}

errors uvádí řádky, které neprošly (tady neplatná adresa na řádku 4).

Pravidla, na která pozor

  • Potlačená adresa (odhlášená, nedoručitelná, stížnost) se importem nikdy znovu nepřihlásí.
  • Pole, která v aplikaci upravil člověk, import nepřepíše.
  • Adresa vymazaná na žádost (GDPR) se importem znovu nezaloží.
  • Import zákazníků bez souhlasu (shoptet_customers bez sloupce souhlasu) založí kontakty customer, kterým se nepíše.