Setup i wdrożenie
Jak działa workflow Vistrify
Ten przewodnik jest dla osób, które chcą sprawdzić dokładnie, jak działa workflow od setupu do publikacji.
Najpierw sprawdź to tutaj
- Darmowy trial obejmuje 1 stronę, a każda płatna strona ma własną subskrypcję.
- Workflow zakłada: strona -> słowa kluczowe -> szkice -> kalendarz -> publikacja.
- Cron generuje szkice do 2 dni przed publikacją, a w dniu publikacji skupia się głównie na samym publish.
1. Szybki start (10 minut)
- Załóż konto i przejdź przez wybór planu dla pierwszej strony.
- Dodaj stronę w zakładce Sites i wybierz rynek docelowy (kraj + język).
- Wygeneruj słowa kluczowe, utwórz szkice, sprawdź treść.
- Podłącz publikację (WordPress lub API), a potem zaplanuj daty w Calendar.
2. Integracja z WordPress (najprostsza)
Użyj tej opcji, jeśli Twój blog działa na WordPress.
- W WordPress przejdź do Users -> Profile -> Application Passwords i utwórz nowe hasło aplikacji.
- W Vistrify: Settings -> Publish connections -> WordPress.
- Uzupełnij pola: wp_url (adres strony), wp_username (użytkownik WP), wp_app_password (hasło aplikacji).
- Kliknij Save WordPress, następnie opublikuj testowo jeden szkic z Drafts -> Publish to WordPress.
Jeśli publikacja się powiedzie, artykuł pojawi się od razu jako wpis w WordPress.
3. Integracja przez Custom API / Webhook
Użyj tej opcji, jeśli nie masz WordPress albo publikujesz do własnego CMS.
- Dodaj webhook_url. To jest główny endpoint publikacji, na który Vistrify wyśle POST z artykułem.
- Opcjonalnie dodaj api_key. Vistrify wyśle go jako nagłówek Authorization: Bearer <token>.
- Jeśli chcesz wspierać update/delete dla już opublikowanych wpisów, ustaw update_webhook_url i delete_webhook_url. Możesz użyć placeholderów {{postId}}, {postId} albo :id.
- Wybierz update_method i delete_method. Domyślnie update używa PUT, a delete używa DELETE.
- Kliknij Save API, potem Test connection. Test wysyła bezpieczny payload i niczego nie publikuje.
Co oznacza każde pole
- webhook_url: wymagany endpoint publikacji.
- api_key: opcjonalny bearer token do autoryzacji.
- update_webhook_url: opcjonalny endpoint aktualizacji. Jeśli go nie podasz, Vistrify spróbuje użyć głównego webhook_url.
- delete_webhook_url: opcjonalny endpoint usuwania. Jeśli go nie podasz, Vistrify spróbuje użyć głównego webhook_url albo znanego publicznego URL wpisu.
- update_method / delete_method: metody HTTP używane przy update/delete. Jeśli Twoja platforma wspiera tylko POST, ustaw POST i rozpoznawaj action w payloadzie.
Minimalny webhook (tylko publikacja)
Najprostsza wersja potrzebuje tylko webhook_url. Endpoint przyjmuje POST, zapisuje artykuł do Twojego CMS lub bazy i zwraca dowolne 2xx.
Jeśli nie zależy Ci jeszcze na późniejszych aktualizacjach i usuwaniu, wystarczy nawet { ok: true }.
Payload publikacji wysyłany przez Vistrify
To jest domyślny JSON wysyłany przez Custom API / Webhook. Niektóre pola są zduplikowane pod różnymi nazwami specjalnie dla kompatybilności z różnymi CMS-ami.
POST /posts
Content-Type: application/json
Authorization: Bearer <api_key> // only if api_key is set
{
"action": "publish",
"id": null,
"postId": null,
"externalPostId": null,
"slug": "article-slug",
"postUrlHint": "https://example.com/blog/article-slug",
"title": "Article title",
"body": "<h1>Article title</h1><p>Rendered HTML content</p>",
"content": "<h1>Article title</h1><p>Rendered HTML content</p>",
"html": "<h1>Article title</h1><p>Rendered HTML content</p>",
"content_html": "<h1>Article title</h1><p>Rendered HTML content</p>",
"markdown": "# Markdown content",
"content_markdown": "# Markdown content",
"body_markdown": "# Markdown content",
"articleTitle": "Article title",
"articleContent": "<h1>Article title</h1><p>Rendered HTML content</p>",
"excerpt": "Short meta description",
"metaDescription": "Short meta description",
"seoDescription": "Short meta description",
"imageUrl": "https://cdn.vistrify.com/covers/generated/draft-123/article-title-v3.png",
"image_url": "https://cdn.vistrify.com/covers/generated/draft-123/article-title-v3.png",
"coverImageUrl": "https://cdn.vistrify.com/covers/generated/draft-123/article-title-v3.png",
"cover_image_url": "https://cdn.vistrify.com/covers/generated/draft-123/article-title-v3.png",
"featuredImage": "https://cdn.vistrify.com/covers/generated/draft-123/article-title-v3.png",
"featured_image": "https://cdn.vistrify.com/covers/generated/draft-123/article-title-v3.png",
"format": "html",
"sourceFormat": "markdown"
}Odpowiedź, której Vistrify oczekuje po publikacji
Do samej publikacji wystarczy dowolny status 2xx. Ale jeśli chcesz, żeby później działał update/delete, zwróć stabilny identyfikator wpisu i najlepiej publiczny URL.
- Akceptowane klucze ID: id, post_id, postId, article_id, articleId, resource_id.
- Akceptowane klucze URL: url, link, post_url, postUrl, permalink, article_url, articleUrl.
HTTP/1.1 201 Created
Content-Type: application/json
{
"ok": true,
"id": "post_123",
"url": "https://example.com/blog/article-slug"
}Pełny webhook CRUD (publikacja + update + delete)
To jest lepsza konfiguracja produkcyjna. Zwracaj ID/URL po publish i ustaw osobne endpointy update/delete albo użyj jednego endpointu z różnymi akcjami.
Jeśli używasz endpointów z placeholderem, Vistrify obsługuje {{postId}}, {postId} i :id. Jeśli placeholdera nie ma, a Vistrify zna ID wpisu, automatycznie dopnie /postId do końca URL.
Contract dla update
Vistrify wysyła ten sam payload co przy publikacji, ale z action = update oraz z ID/URL już opublikowanego wpisu.
Domyślnie update używa PUT. Jeśli endpoint zwraca błąd albo Twoja platforma woli POST, Vistrify potrafi ponowić próbę jako POST.
PUT /posts/{{postId}}
Content-Type: application/json
Authorization: Bearer <api_key> // only if api_key is set
{
"...publishFields": "same fields as publish payload",
"action": "update",
"id": "post_123",
"postId": "post_123",
"externalPostId": "post_123",
"url": "https://example.com/blog/article-slug",
"postUrl": "https://example.com/blog/article-slug",
"externalPostUrl": "https://example.com/blog/article-slug"
}Contract dla delete
Domyślnie Vistrify wywołuje delete endpoint metodą DELETE. Jeśli ustawisz delete_method = POST, payload delete jest wysyłany jako JSON.
Jeśli endpoint DELETE odpowie 405 albo 501, Vistrify automatycznie spróbuje jeszcze raz metodą POST z action = delete.
DELETE /posts/{{postId}}
Authorization: Bearer <api_key> // only if api_key is set
// If delete_method is POST instead of DELETE, Vistrify sends JSON like:
{
"action": "delete",
"id": "post_123",
"postId": "post_123",
"externalPostId": "post_123",
"slug": "article-slug",
"url": "https://example.com/blog/article-slug",
"postUrl": "https://example.com/blog/article-slug",
"externalPostUrl": "https://example.com/blog/article-slug",
"title": "Article title",
"articleTitle": "Article title",
"body": "# Markdown content",
"content": "# Markdown content",
"markdown": "# Markdown content",
"articleContent": "# Markdown content",
"format": "markdown"
}Jak działa Test connection
Przycisk Test connection zawsze wysyła bezpieczny request do webhook_url. Nie publikuje treści i możesz potraktować go jak prosty health check z auth.
POST /posts
Content-Type: application/json
X-Vistrify-Test: 1
Authorization: Bearer <api_key> // only if api_key is set
{
"action": "test_connection"
}Przykład działającego endpointu Node / Express
Ten przykład pokazuje pełny wariant publish + update + delete. Zastąp Mapę własnym CMS-em, bazą danych albo API.
import express from "express";
import crypto from "node:crypto";
const app = express();
app.use(express.json());
const posts = new Map();
function publicUrl(slug) {
return `https://your-site.com/blog/${slug}`;
}
app.post("/posts", (req, res) => {
if (req.header("X-Vistrify-Test") === "1" || req.body?.action === "test_connection") {
return res.json({ ok: true });
}
const id = crypto.randomUUID();
const slug = req.body.slug || id;
posts.set(id, {
id,
slug,
title: req.body.title,
body: req.body.content_html || req.body.html || req.body.body || "",
excerpt: req.body.metaDescription || req.body.excerpt || "",
imageUrl: req.body.imageUrl || req.body.coverImageUrl || "",
});
return res.status(201).json({
ok: true,
id,
url: publicUrl(slug),
});
});
app.put("/posts/:id", (req, res) => {
const existing = posts.get(req.params.id);
if (!existing) return res.status(404).json({ ok: false, error: "Not found" });
const slug = req.body.slug || existing.slug;
const nextPost = {
...existing,
slug,
title: req.body.title,
body: req.body.content_html || req.body.html || req.body.body || existing.body,
excerpt: req.body.metaDescription || req.body.excerpt || existing.excerpt,
imageUrl: req.body.imageUrl || req.body.coverImageUrl || existing.imageUrl,
};
posts.set(req.params.id, nextPost);
return res.json({
ok: true,
id: req.params.id,
url: publicUrl(slug),
});
});
app.delete("/posts/:id", (req, res) => {
if (!posts.has(req.params.id)) {
return res.status(404).json({ ok: false, error: "Not found" });
}
posts.delete(req.params.id);
return res.json({ ok: true, deleted: true, id: req.params.id });
});
app.listen(3000, () => {
console.log("Webhook listening on http://localhost:3000");
});Przykład jednego endpointu POST-only
Użyj tego wariantu, jeśli Twój CMS albo backend obsługuje tylko POST. W Settings ustaw webhook_url, update_webhook_url i delete_webhook_url na ten sam adres, a update_method oraz delete_method ustaw na POST.
W tym modelu rozgałęziasz logikę po polu action: publish, update, delete albo test_connection.
import express from "express";
import crypto from "node:crypto";
const app = express();
app.use(express.json());
const posts = new Map();
function publicUrl(slug) {
return `https://your-site.com/blog/${slug}`;
}
app.post("/posts", (req, res) => {
const action = req.body?.action;
if (req.header("X-Vistrify-Test") === "1" || action === "test_connection") {
return res.json({ ok: true });
}
if (action === "publish") {
const id = crypto.randomUUID();
const slug = req.body.slug || id;
posts.set(id, {
id,
slug,
title: req.body.title,
body: req.body.content_html || req.body.html || req.body.body || "",
excerpt: req.body.metaDescription || req.body.excerpt || "",
imageUrl: req.body.imageUrl || req.body.coverImageUrl || "",
});
return res.status(201).json({
ok: true,
id,
url: publicUrl(slug),
});
}
if (action === "update") {
const id = req.body.postId || req.body.id || req.body.externalPostId;
const existing = id ? posts.get(id) : null;
if (!id || !existing) {
return res.status(404).json({ ok: false, error: "Not found" });
}
const slug = req.body.slug || existing.slug;
const nextPost = {
...existing,
slug,
title: req.body.title,
body: req.body.content_html || req.body.html || req.body.body || existing.body,
excerpt: req.body.metaDescription || req.body.excerpt || existing.excerpt,
imageUrl: req.body.imageUrl || req.body.coverImageUrl || existing.imageUrl,
};
posts.set(id, nextPost);
return res.json({
ok: true,
id,
url: publicUrl(slug),
});
}
if (action === "delete") {
const id = req.body.postId || req.body.id || req.body.externalPostId;
if (!id || !posts.has(id)) {
return res.status(404).json({ ok: false, error: "Not found" });
}
posts.delete(id);
return res.json({ ok: true, deleted: true, id });
}
return res.status(400).json({ ok: false, error: "Unsupported action" });
});
app.listen(3000, () => {
console.log("POST-only webhook listening on http://localhost:3000/posts");
});Najważniejsze zasady
- Jeśli chcesz update/delete, zwracaj stabilne id po publikacji.
- Najlepiej zwracaj też publiczny URL wpisu, bo pomaga to w kolejnych operacjach i weryfikacji.
- Jeśli Twój system wspiera tylko POST, ustaw update/delete na POST i rozpoznawaj pole action.
- Endpoint musi naprawdę zapisywać, aktualizować albo usuwać wpis. Samo 200 OK bez zmian w CMS nie wystarczy.
4. Jak działa auto-publikacja
- W Calendar przeciągnij słowa kluczowe na konkretne dni albo użyj Auto-schedule.
- Dzienny cron generuje szkice do 2 dni wcześniej, a w dniu publikacji wykorzystuje gotowy draft do samej publikacji.
- Jeśli publikacja się nie powiedzie, szkic zostaje wygenerowany i czeka na ręczne kliknięcie Publish.
5. Najczęstsze błędy i szybkie rozwiązania
- 401 / 403: niepoprawny login, hasło aplikacji lub token API.
- 404: zły adres webhooka lub endpoint nie istnieje.
- 502: Vistrify nie może połączyć się z Twoim endpointem albo endpoint zwrócił błąd.
- "No connection configured": połączenie publikacji nie zostało zapisane dla aktywnej strony.
- Wpis nie pojawia się na stronie: endpoint przyjmuje dane, ale nie zapisuje ich do CMS/bazy.
- Update/delete nie działa: endpoint publish nie zwraca stabilnego id/url albo endpoint update/delete ignoruje postId.
- Test connection nie przechodzi: sprawdź, czy firewall/WAF nie blokuje POST lub nagłówka X-Vistrify-Test.
6. Wsparcie
Jeśli chcesz, abyśmy sprawdzili integrację krok po kroku, napisz na info@vistrify.com i podaj domenę oraz typ integracji (WordPress lub API).
Następny krok
Jeśli setup wygląda sensownie, uruchom jedną stronę
Najpierw sprawdź dokumentację, potem uruchom jeden workflow i oceń produkt po wykonaniu, nie po obietnicach.
14-dniowy darmowy trial. Bez karty. 1 strona. Ograniczony limit triala.