Docs durchsuchen

Guides und API-Endpunkte durchsuchen

Referenz

Versandjobs

Ein Newsletter geht an viele Empfänger und wird als Versandjob verschickt: anlegen (Template, Parameter, Absender, optionaler Startzeitpunkt), Empfänger in Blöcken anhängen, starten, abfragen. Ein Worker rendert einmal, schickt Batches an Postmark und hält je Empfänger das Ergebnis fest. Die Empfängerliste baut das Kundensystem aus seinem CRM — gefiltert nach Einwilligung und Sperre.

Verfügbar
POST

/v1/send-jobs

Legt einen Job als Entwurf an. Das Template muss dem Kunden gehören, aktiv und ein Newsletter sein; params sind die Werte seiner Template-Parameter (nur bekannte). Absender: Standard ist die Newsletter-Adresse des Kunden oder die Ausnahme des Templates; from.email darf den Teil vor dem @ ändern. scheduled_at plant den Start. Scope full, idempotent.

Body-Parameter

NameTypPflichtBeschreibung
template_idstring (UUID)erforderlichEin Newsletter-Template des Kunden. z. B. 00000000-0000-4000-8000-0000000000f4
paramsobjectoptionalWerte der Template-Parameter. {{platzhalter}} in festen Texten werden aus params und den Daten des Empfängers gefüllt — der Empfänger gewinnt. z. B. {"titel":"Sommerprogramm"}
fromobjectoptionalAbsender-Ausnahme { email, name } auf einer Domain des Kunden. z. B. {"email":"lena.berger@kunde.de","name":"Lena Berger"}
reply_tostring (E-Mail)optionalAntwort-Adresse; leer = wie Absender. z. B. redaktion@kunde.de
scheduled_atstring (ISO 8601)optionalStartzeitpunkt; leer = sofort beim Start. z. B. 2026-09-05T08:00:00Z

Request · Next.js

const res = await fetch(`https://api.newsletterkit.ventureon.io/v1/send-jobs`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NEWSLETTERKIT_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      template_id: 00000000-0000-4000-8000-0000000000f4,
      params: {"titel":"Sommerprogramm"},
      from: {"email":"lena.berger@kunde.de","name":"Lena Berger"},
      reply_to: "redaktion@kunde.de",
      scheduled_at: 2026-09-05T08:00:00Z,
    }),
});
const data = await res.json();

Response 200

{
  "data": {
    "id": "f90c0326-4a19-4b9f-bf81-37145e6371ab",
    "status": "draft",
    "mode": "live",
    "template_id": "00000000-0000-4000-8000-0000000000f4",
    "params": { "titel": "Sommerprogramm" },
    "from": { "email": "news@kunde.de", "name": "Kunde" },
    "reply_to": null,
    "scheduled_at": "2026-09-05T08:00:00Z",
    "recipients": { "total": 0, "accepted": 0, "suppressed": 0, "invalid": 0, "failed": 0, "pending": 0 },
    "batches": null,
    "created_at": "2026-09-03T16:00:00Z"
  },
  "request_id": "…"
}

Status: draft → scheduled | running → completed | cancelled | paused | failed. Ein Job im Sandbox-Modus des Kunden wird von Postmark angenommen, aber nicht zugestellt und nicht gezählt.

POST

/v1/send-jobs/{id}/recipients

Hängt Empfänger an einen Job im Entwurf an — bis 5.000 je Aufruf, größere Listen in mehreren Aufrufen. Jeder Empfänger bekommt sofort seine Mail-ID; das Portal schreibt sie an seinen Kontakt. Eine Adresse ist je Job nur einmal drin (Dubletten werden gemeldet, nicht doppelt angelegt). data sind Werte für {{platzhalter}} dieses Empfängers, z. B. vorname. Mit einem Idempotency-Key je Block erzeugt ein Retry keine Dubletten. Scope full.

Path-Parameter

NameTypPflichtBeschreibung
idstring (UUID)erforderlichDie Job-ID. z. B. f90c0326-4a19-4b9f-bf81-37145e6371ab

Body-Parameter

NameTypPflichtBeschreibung
recipientsobject[]erforderlich[{ email, data? }], 1–5.000 Einträge; data ist ein flaches Objekt aus Strings. z. B. [{"email":"mira@example.com","data":{"vorname":"Mira"}}]

Request · Next.js

const id = "f90c0326-4a19-4b9f-bf81-37145e6371ab";

const res = await fetch(`https://api.newsletterkit.ventureon.io/v1/send-jobs/${id}/recipients`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NEWSLETTERKIT_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      recipients: [{"email":"mira@example.com","data":{"vorname":"Mira"}}],
    }),
});
const data = await res.json();

Response 200

{
  "data": {
    "added": 2,
    "duplicates": 0,
    "total": 2,
    "recipients": [
      { "email": "mira@example.com", "message_id": "33267271-…" },
      { "email": "max@example.com",  "message_id": "42ba5d6b-…" }
    ]
  },
  "request_id": "…"
}

429 quota_exceeded, wenn die Höchstzahl je Job des Kunden überschritten würde. 409, wenn der Job nicht mehr im Entwurf ist.

POST

/v1/send-jobs/{id}/start

Startet den Job — sofort oder zum Zeitpunkt in scheduled_at (Body oder beim Anlegen gesetzt). Prüft Freischaltung, Newsletter-Footer, Pflicht-Parameter und im Live-Modus das Monatslimit. Danach übernimmt der Worker: einmal rendern, Batches an Postmark. Scope full.

Path-Parameter

NameTypPflichtBeschreibung
idstring (UUID)erforderlichDie Job-ID. z. B. f90c0326-4a19-4b9f-bf81-37145e6371ab

Body-Parameter

NameTypPflichtBeschreibung
scheduled_atstring (ISO 8601)optionalStartzeitpunkt; überschreibt den beim Anlegen gesetzten. z. B. 2026-09-05T08:00:00Z

Request · Next.js

const id = "f90c0326-4a19-4b9f-bf81-37145e6371ab";

const res = await fetch(`https://api.newsletterkit.ventureon.io/v1/send-jobs/${id}/start`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NEWSLETTERKIT_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      scheduled_at: 2026-09-05T08:00:00Z,
    }),
});
const data = await res.json();

Response 200

{
  "data": { "id": "f90c0326-…", "status": "running", "recipients": { "total": 12000, "accepted": 0, "pending": 12000, "suppressed": 0, "invalid": 0, "failed": 0 } },
  "request_id": "…"
}

403, wenn der Versand nicht freigeschaltet ist; 422 ohne Empfänger oder bei fehlenden Pflicht-Parametern; 429 quota_exceeded am Monatslimit.

POST

/v1/send-jobs/{id}/cancel

Bricht einen Job ab — im Entwurf, geplant, laufend oder pausiert. Was Postmark schon angenommen hat, ist raus; alles Übrige geht nicht mehr raus. Scope full.

Path-Parameter

NameTypPflichtBeschreibung
idstring (UUID)erforderlichDie Job-ID. z. B. f90c0326-4a19-4b9f-bf81-37145e6371ab

Request · Next.js

const id = "f90c0326-4a19-4b9f-bf81-37145e6371ab";

const res = await fetch(`https://api.newsletterkit.ventureon.io/v1/send-jobs/${id}/cancel`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NEWSLETTERKIT_API_KEY}`,
      "Idempotency-Key": crypto.randomUUID(),
    },
});
const data = await res.json();

Response 200

{
  "data": { "id": "f90c0326-…", "status": "cancelled", "cancelled_at": "2026-09-03T16:20:00Z" },
  "request_id": "…"
}
GET

/v1/send-jobs/{id}

Der Job mit Zählern je Ergebnis (angenommen, gesperrt, ungültig, fehlgeschlagen, offen), den Summen aus den Postmark-Ereignissen (stats) und denselben Zahlen je Stunde (timeline). Während des Versands alle paar Sekunden abfragen, bis der Status completed ist; danach ist es die Auswertungsseite. Scope reporting.

Path-Parameter

NameTypPflichtBeschreibung
idstring (UUID)erforderlichDie Job-ID. z. B. f90c0326-4a19-4b9f-bf81-37145e6371ab

Request · Next.js

const id = "f90c0326-4a19-4b9f-bf81-37145e6371ab";

const res = await fetch(`https://api.newsletterkit.ventureon.io/v1/send-jobs/${id}`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${process.env.NEWSLETTERKIT_API_KEY}`,
    },
});
const data = await res.json();

Response 200

{
  "data": {
    "id": "f90c0326-…",
    "status": "completed",
    "mode": "live",
    "recipients": { "total": 12000, "accepted": 11812, "suppressed": 188, "invalid": 0, "failed": 0, "pending": 0 },
    "batches": 68,
    "stats": { "accepted": 11812, "suppressed": 188, "delivered": 11640, "bounced": 92, "opened": 6120, "opened_unique": 4870, "clicked": 1420, "clicked_unique": 1180, "complained": 6, "unsubscribed": 41 },
    "timeline": [ { "bucket": "2026-09-05T08:00:00Z", "accepted": 11812, "delivered": 9800, "opened": 1200, "…": "…" } ]
  },
  "request_id": "…"
}

Öffnungen und Klicks kommen aus Postmarks Ereignissen und laufen noch Tage nach dem Versand ein. Ein pausierter Job nennt in paused_reason, was ein Admin klären muss.

GET

/v1/send-jobs/{id}/messages

Die Mails eines Jobs mit ihrem Zustand, über Mail-IDs — etwa alle gebouncten (status=bounced). Die Adresse steht nur dabei, solange der Job läuft; nach dem Abschluss kennt sie nur noch das Portal. Cursor über after. Scope reporting.

Path-Parameter

NameTypPflichtBeschreibung
idstring (UUID)erforderlichDie Job-ID. z. B. f90c0326-4a19-4b9f-bf81-37145e6371ab

Query-Parameter

NameTypPflichtBeschreibung
statusenumoptionalpending | accepted | suppressed | invalid | failed | unknown | cancelled | delivered | bounced | complained z. B. bounced
limitintegeroptionalEinträge pro Seite, max. 500. z. B. 500
afterstringoptionalCursor aus paging.after. z. B. MjAyNi0…

Request · Next.js

const id = "f90c0326-4a19-4b9f-bf81-37145e6371ab";

const res = await fetch(`https://api.newsletterkit.ventureon.io/v1/send-jobs/${id}/messages?status=bounced&limit=500&after=MjAyNi0%E2%80%A6`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${process.env.NEWSLETTERKIT_API_KEY}`,
    },
});
const data = await res.json();

Response 200

{
  "data": [
    { "message_id": "00024021-…", "status": "bounced", "email": null, "bounce_type": "HardBounce", "bounced_at": "2026-09-05T08:04:12Z", "delivered_at": null, "first_opened_at": null, "first_clicked_at": null, "complained_at": null, "error": null }
  ],
  "paging": { "after": null, "has_next": false },
  "request_id": "…"
}
GET

/v1/send-jobs

Die letzten Jobs, neueste zuerst; optional nach status gefiltert. Scope reporting.

Query-Parameter

NameTypPflichtBeschreibung
statusenumoptionaldraft | scheduled | running | paused | completed | cancelled | failed z. B. running
limitintegeroptionalmax. 100, Standard 25. z. B. 25

Request · Next.js

const res = await fetch(`https://api.newsletterkit.ventureon.io/v1/send-jobs?status=running&limit=25`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${process.env.NEWSLETTERKIT_API_KEY}`,
    },
});
const data = await res.json();

Response 200

{
  "data": [ { "id": "f90c0326-…", "status": "running", "recipients": { "total": 12000, "accepted": 8673, "pending": 3327, "suppressed": 0, "invalid": 0, "failed": 0 } } ],
  "request_id": "…"
}