Canva Connect API и n8n: автоматизирани дизайни
Един шаблон, хиляда варианта. Подготвяш дизайн в Canva, а n8n го попълва с текст и снимки, изчаква Canva да го сглоби, изнася го като PNG и го записва. Тук минаваме целия път: влизането с OAuth 2.0, шаблоните, заявките, лимитите и честния въпрос „кой план ми трябва“.
POST /autofills с brand_template_id в тялото (не /designs/{id}/autofill); export е POST /exports с design_id в тялото; качването на файл е POST /asset-uploads; няма заявка OPTIONS за полетата — има GET /brand-templates/{id}/dataset. Вход: Canva иска OAuth 2.0 с PKCE и Authorization URL https://www.canva.com/api/oauth/authorize — старият адрес /oauth/authorize е грешен; localhost в пренасочването е заменен с препоръчания от Canva 127.0.0.1. Scopes: export:read не съществува; нужни са design:content:read за export и brandtemplate:* за шаблоните. Планове: „само Teams или Pro“ беше неточно — виж раздел „Преди да започнеш“. Лимити: „~60 заявки/минута“ не е общ лимит; всеки endpoint има свой (export — 20 в минута на потребител). Добавени са polling с backoff, качване на снимки, Canva MCP и бележки за n8n. Сигурността: в стария код имаше Bearer YOUR_TOKEN в командния ред — сега токените стоят само в credential на n8n.
01Какво ще научиш
- Как е устроен пътят „шаблон → autofill → export → файл“ и кои заявки го изграждат.
- Как Canva те пуска вътре: интеграция в Developer Portal, scopes, OAuth 2.0 с PKCE и токени, които се подновяват.
- Как се свързва това в n8n, без да пишеш своя сървър за вход.
- Как се подготвя brand шаблон с именувани полета и как се качва снимка, за да влезе в него.
- Как да изчакаш асинхронна задача с polling и backoff и как да планираш масово генериране така, че лимитите да не те спрат.
- Кой план ти трябва за какво — и какво правиш, ако нямаш такъв (Canva MCP или HTML → PNG).
02Преди да започнеш
- Работещ n8n 🔒 локално от Обучение 3 и достъп до неговия интерфейс.
- Акаунт в Canva 🌐 глобален с включена двустепенна защита (MFA) — ръководството за autofill я изисква за акаунта, с който разработваш.
- Достъп до Developer Portal на Canva, където ще създадеш приложението си.
- Ясен отговор на въпроса за плана — таблицата по-долу.
| Какво искаш | Какво пише в документацията на Canva |
|---|---|
| Списък, четене и export на дизайни, качване на файлове | Работи и с безплатен акаунт. Платен план ти трябва само за премиум части: export_quality: "pro", PNG с прозрачен фон и PNG със загуба на качество. Безплатният акаунт може да увеличава размера при export най-много 1,125 пъти. |
| Brand шаблони и autofill (попълване) | Справочникът на API и ръководството за autofill вече казват едно и също: нужен е план с autofill — Canva Pro (вкл. Canva Education и Canva for Nonprofits), Canva Teams или Canva Enterprise. Canva предупреждава, че в бъдеще ще въведе лимити за ползване. Провери със своя акаунт с първата заявка към /brand-templates. |
| Частно приложение само за твоя екип | Частните приложения са налични само за екипи с Canva Enterprise. Публично приложение минава преглед от Canva. |
| Canva през AI асистент (MCP) | Ядрото — търсене, създаване, export, коментари — е за всички планове. Инструментите за autofill и brand шаблони са „Pro и нагоре“. |
/brand-templates със своя токен. Ако получиш 403 с текст за липсваща възможност (capability), планът ти не го включва — тогава ползвай резервния път в края на урока.03Стъпки
-
Картата на пътя
Canva Connect API е REST интерфейс. Всичко върви през базов адрес
https://api.canva.com/rest/v1, а всяка заявка носиAuthorization: Bearer <токен>— токен, издаден от името на конкретен Canva потребител. Защо е важно: автоматизацията ти няма „свой“ достъп, тя действа като човека, който я е разрешил — със неговия план и неговите папки.Двете дълги операции — autofill и export — са асинхронни задачи: изпращаш заявка, получаваш
job.idсъс статусin_progressи питаш втори endpoint, докато станеsuccessилиfailed.Метод и път За какво е Лимит на потребител GET /designsСписък с дизайни (до 100 на страница, continuationза следващата)100 / мин GET /brand-templatesСписък с brand шаблоните, до които имаш достъп 100 / мин GET /brand-templates/{id}/datasetИмена и типове на полетата за попълване 100 / мин POST /asset-uploads·GET /asset-uploads/{jobId}Качване на снимка, после проверка на задачата 30 / мин · 180 / мин POST /autofills·GET /autofills/{jobId}Попълва шаблон → нов дизайн; проверка на задачата 60 / мин · 120 / мин POST /exports·GET /exports/{exportId}Export на дизайн (PNG, JPG, PDF, PPTX, GIF, MP4…); проверка и адреси за сваляне 20 / мин · 120 / мин POST /foldersСъздава папка 20 / мин пътят в една линияbrand шаблон → POST /autofills → GET /autofills/{id} → design.id → POST /exports → GET /exports/{id} → urls[0] → файл -
Създай приложение в Developer Portal
В Your apps избери Create an app: име до 18 знака; за „кой може да го ползва“ избираш Private само ако си в Enterprise екип — иначе Public (и това не се сменя после). После: Outside Canva → Start integrating, в Configuration провери, че Canva REST APIs е включено, натисни Generate secret и запази тайната на сигурно място — показва се само веднъж.
Включи scopes (права) в същия екран. Защо минимални: ако токенът изтече, вредата е колкото правата му.
scopes · за урокаdesign:content:read # export на дизайни design:content:write # autofill: създава нови дизайни design:meta:read # състояние на autofill задачи, метаданни brandtemplate:meta:read # списък с brand шаблони brandtemplate:content:read # полетата (dataset) на шаблона asset:read # проверка на качени файлове asset:write # качване на снимки за autofill✅Правило за scopesИзброявай ги изрично:asset:writeне даваasset:read. Заявка за scope, който не е включен в портала, се отхвърля.В Redirect URLs въведи адреса, на който Canva връща потребителя след съгласието. Пътят за n8n: n8n показва своя „OAuth Redirect URL“ в формата за credential — копираш го оттам. Официалните примери на Canva ползват
http://127.0.0.1:<порт>, а неlocalhost(сlocalhostсе получават грешки с CORS). ⚠️ Далиlocalhostсе отхвърля изрично, не е проверено. За продукция ползвайhttpsадрес, който контролираш, и махни локалните адреси, преди да подаваш публично приложение. -
Как работи входът: OAuth 2.0 с PKCE
Canva ползва Authorization Code flow с PKCE (SHA-256). Накратко: създаваш таен низ (
code_verifier), пращаш само хеша му (code_challenge) и пращаш потребителя към Canva. След съгласие Canva го връща при теб с еднократенcode. Ти го сменяш за токен, като показваш оригиналния низ. Защо: откраднатcodeбезcode_verifierе безполезен.В n8n не пишеш това на ръка — credential-ът го прави вместо теб (следващата стъпка). Но е добре да го виждаш веднъж, за да разбереш грешките:
Node.js · code_verifier и code_challenge (по документацията на Canva)import crypto from "crypto"; const codeVerifier = crypto.randomBytes(96).toString("base64url"); const codeChallenge = crypto .createHash("sha256") .update(codeVerifier) .digest("base64url"); const state = crypto.randomBytes(96).toString("base64url");адрес за съгласие (нов ред за четимост;<…>се заменят)https://www.canva.com/api/oauth/authorize ?code_challenge=<codeChallenge> &code_challenge_method=s256 &scope=design:content:read%20design:content:write%20design:meta:read &response_type=code &client_id=<CLIENT_ID> &state=<state> &redirect_uri=<REDIRECT_URI>bash · смяна на code за токен (само от сървър, никога от браузър)curl --request POST 'https://api.canva.com/rest/v1/oauth/token' \ --header "Authorization: Basic $(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64 -w0)" \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=authorization_code' \ --data-urlencode "code=$AUTH_CODE" \ --data-urlencode "code_verifier=$CODE_VERIFIER" \ --data-urlencode "redirect_uri=$REDIRECT_URI"⚠️Капани при токенитеAccess токенът живее кратко. За нов използвашrefresh_tokenсъсgrant_type=refresh_token. Всеки refresh токен важи само веднъж: отговорът връща нов access и нов refresh токен — старият вече не върши работа, затова и двата трябва да се записват. Заявките съсclient_secretидват само от твоя сървър; от браузър Canva ги блокира (CORS). Тайни и токени не се слагат в скриптове, в git и в експортирания JSON на работен процес. -
Свържи Canva в n8n
В n8n: Credentials → New → OAuth2 API (общият тип за HTTP Request). Избери Grant Type: PKCE — n8n го поддържа и сам прави
code_verifierиcode_challenge. Authentication: Header праща клиента като Basic — точно каквото иска токен endpoint-ът на Canva.n8n · OAuth2 API credential · „Canva“Grant Type: PKCE Authorization URL: https://www.canva.com/api/oauth/authorize Access Token URL: https://api.canva.com/rest/v1/oauth/token Client ID: <CLIENT_ID от Developer Portal> Client Secret: <CLIENT_SECRET — само тук, в n8n> Scope: design:content:read design:content:write design:meta:read brandtemplate:meta:read brandtemplate:content:read asset:read asset:write Authentication: HeaderКопирай „OAuth Redirect URL“ от формата в полето Redirect URLs на Canva, запази и натисни Connect my account. Ще видиш екрана на Canva със съгласие; след него credential-ът е зелен. ⚠️ Тази връзка не е пускана на живо в този урок — ако Canva върне грешка за
code_challenge_method, прегледай полето Auth URI Query Parameters в credential-а.💡Защо не „Authorization Code“ без PKCEСтарата версия на урока избираше „Authorization Code“. Canva изисква PKCE, затова този избор не може да мине през проверката наcode_verifier. -
Подготви brand шаблон и научи полетата му
Autofill не работи върху произволен дизайн, а върху публикуван brand шаблон с именувани полета за данни. Проектирай пост 1080×1080 с текстове (заглавие, подзаглавие, призив) и място за картинка, отбележи ги като полета за данни (помощ на Canva за data autofill) и публикувай дизайна като brand шаблон. ID-то е краят на адреса:
canva.com/brand/brand-templates/AEN3TrQftXo.Имената на полетата не се гадаят. Питаш шаблона:
bash · полета на шаблонаcurl --request GET \ --url "https://api.canva.com/rest/v1/brand-templates/<TEMPLATE_ID>/dataset" \ --header "Authorization: Bearer $CANVA_TOKEN" # пример за отговор: # { "dataset": { "TITLE": {"type":"text"}, "SUBTITLE": {"type":"text"}, "BACKGROUND": {"type":"image"} } }✅Правило за полетатаНе е нужно да попълниш всички — непопълнените остават със стойността от шаблона. Но име, което не съществува, Canva мълчаливо пропуска. Затова първо питай за dataset, после пиши заявката.Снимки: за поле от тип
imageподавашasset_idна вече качен файл — външен адрес директно не се приема. Качваш презPOST /asset-uploads(двоично съдържание и хедърAsset-Upload-Metadataс името в Base64), после питашGET /asset-uploads/{jobId}доsuccess— в отговора еasset.id. Ако снимката е на адрес в мрежата, ползвайPOST /url-asset-uploads. -
Работният процес: autofill → export → файл
Осем стъпки са достатъчни. Обърни внимание на двата цикъла за изчакване — те замениха „Wait 3s“ от стария урок, който е предположение, а не гаранция.
възли по ред1 Manual Trigger 2 Edit Fields (title, subtitle, asset_id) 3 HTTP Request "Autofill" 4 Code "Backoff" → Wait → HTTP Request "Autofill job" → If success? (цикъл) 5 HTTP Request "Export" 6 Code "Backoff" → Wait → HTTP Request "Export job" → If success? (цикъл) 7 HTTP Request "Свали файла" (Response Format: File) 8 Read/Write Files from Disk (Write)В HTTP Request възлите: Authentication → Generic Credential Type → OAuth2 API → „Canva“, Send Body → JSON.
n8n · HTTP Request „Autofill“ · POST https://api.canva.com/rest/v1/autofills{ "brand_template_id": "<TEMPLATE_ID>", "data": { "TITLE": { "type": "text", "text": "{{ $json.title }}" }, "SUBTITLE": { "type": "text", "text": "{{ $json.subtitle }}" }, "BACKGROUND": { "type": "image", "asset_id": "{{ $json.asset_id }}" } } } // отговор: { "job": { "id": "…", "status": "in_progress" } }n8n · HTTP Request „Autofill job“ · GET …/autofills/{{ $('Autofill').item.json.job.id }}// успех: job.status = "success" // дизайнът е в job.result.design : { id, url, thumbnail… } // грешка: job.status = "failed" → job.error.code (напр. autofill_error)n8n · HTTP Request „Export“ · POST https://api.canva.com/rest/v1/exports{ "design_id": "{{ $('Autofill job').item.json.job.result.design.id }}", "format": { "type": "png", "width": 1080 } } // export_quality е по подразбиране "regular"; "pro" може да се провали без платен план // отговор: { "job": { "id": "…", "status": "in_progress" } }n8n · HTTP Request „Export job“ · GET …/exports/{{ $('Export').item.json.job.id }}// успех: job.status = "success", job.urls = ["https://…"] (важат 24 часа) // грешка: job.error.code = license_required | approval_required | internal_failureПоследно: HTTP Request със
GETвърху{{ $json.job.urls[0] }}и Response Format → File, после Read/Write Files from Disk (Write) в папка, която е монтирана в контейнера на n8n — иначе файлът остава вътре в контейнера и го губиш при рестарт. -
Изчакването: polling с backoff
Canva препоръчва експоненциално нарастващ интервал: започваш бързо, удължаваш паузата до таван. Така не удряш лимитите (проверката на export е 120 в минута) и не висиш излишно. Цикълът в n8n: Code → Wait → HTTP (проверка) → If (
success?). При „не“ — връщаш към Code. Приfailedили достигнат таван — стоп със грешка.n8n · Code възел „Backoff“ · Run Once for Each Itemconst attempt = $runIndex + 1; // кой път минава през този възел if (attempt > 8) { throw new Error("Canva: задачата не приключи след 8 проверки"); } return { json: { ...$json, attempt, waitSeconds: Math.min(2 ** attempt, 20) } }; // Wait възелът: Resume = After Time Interval, Amount = {{ $json.waitSeconds }}, Unit = Seconds💡Защо таван от 8 проверкиПри 2, 4, 8, 16, 20… секунди това е около две минути общо — повече от достатъчно за един дизайн. Ако задача виси по-дълго, по-вероятно има грешка, а не бавен сървър. Числата са разумен избор, не правило на Canva. -
Масово генериране и лимитите
Десет продукта — десет дизайна. Вземаш редове от Google Sheets или CSV, подаваш ги през Loop Over Items (бившият Split In Batches) с размер 1 и за всеки минаваш целия път. Тесният момент е export: лимитът е 20 заявки в минута на потребител, значи най-много една на всеки 3 секунди.
Ограничение при export Стойност Потребител (през твоята интеграция) 20 / мин · 75 на 5 мин · 500 на 24 ч Цялата интеграция 750 на 5 мин · 5 000 на 24 ч Един и същ дизайн 75 на 5 мин ⚠️Лимитът не е „60 на минута за всичко“Всеки endpoint има собствен лимит на потребител в минута (таблицата в стъпка 1). Autofill е 60, export е 20. При надвишаване Canva връща429с кодtoo_many_requests— в n8n включи Retry On Fail за HTTP възлите и добави Wait от 3–4 секунди между дизайните.Един ден със 100 дизайна е безопасен: 100 export-а е много под 500 на ден за потребител. При хиляди — раздели на дни или потребители.
-
Canva през AI асистент: Canva MCP
Ако искаш да кажеш на асистент „направи ми пост от този шаблон“, не ти трябва собствен n8n процес. Canva предлага отдалечен MCP сървър на адрес
https://mcp.canva.com/mcp🌐 глобален: асистентът получава инструменти за търсене и създаване на дизайни, качване на файлове, коментари и export; всеки има свой лимит в минута (напр. export и създаване — 20, търсене — 100). Автоматичното попълване на brand шаблони е „Pro и нагоре“; export работи на всички планове, но премиум качество — не.Как се свързва конкретен асистент (Claude, ChatGPT, Cursor…) показва помощният център на Canva. Всеки потребител влиза със свой акаунт. Кога кое: MCP — за диалог и единични дизайни; Connect API през n8n — за повтарящ се, неуправляван от човек процес.
-
Без подходящ план: HTML → PNG
Ако autofill не ти е достъпен, остава път, който не зависи от Canva: HTML шаблон + безглав браузър. По-беден е визуално, но е безплатен и 🔒 напълно локален. Пример с Puppeteer (изисква Node.js 22 или по-нов):
bash + Node.js · html-to-image.jsmkdir -p ~/designs && cd ~/designs npm install puppeteer cat > html-to-image.js << 'EOF' const puppeteer = require('puppeteer'); const [htmlFile, outFile = 'design.png'] = process.argv.slice(2); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setViewport({ width: 1080, height: 1080 }); await page.goto('file://' + htmlFile); await page.screenshot({ path: outFile }); await browser.close(); console.log(outFile); })(); EOF node html-to-image.js "$PWD/template.html" "$PWD/post.png"⚠️Две уговорки за n8nВъзелът Execute Command е изключен по подразбиране в n8n (той е в списъкаNODES_EXCLUDE), защото може да изпълни всичко на сървъра. Включването му е съзнателно решение за инстанция, на която вярваш. По-чисто: пусни скрипта отделно (планировчик, втори контейнер) и остави n8n да чете готовия файл. И още — рендирай само свой HTML: страница от чужд източник в безглав браузър е риск. Ако в контейнер Puppeteer откаже да стартира от root, му трябва флагът--no-sandbox— ползвай го само там и само за свой HTML.
04Проверка
Чеклист
- Приложението е създадено, REST APIs е включено, scopes са изброени, redirect URL е записан, тайната е на сигурно място.
- Credential-ът в n8n е с PKCE и Header; „Connect my account“ минава през екрана за съгласие.
- Шаблонът е публикуван, а
/datasetвръща очакваните имена на полета. - Autofill задачата стига до
successи връщаdesign.id. - Export задачата стига до
successи файлът е свален в срок от 24 часа. - Цикълът за изчакване има backoff и твърд таван.
- Масовият процес не праща повече от около един export на 3 секунди.
- В експортирания JSON на работния процес няма тайни или токени.
Тест
1. Какъв вход ползва Canva Connect API?
2. POST /autofills върна in_progress. Какво правиш?
3. Колко заявки POST /exports може да прави един потребител на минута?
4. Какво е вярно за refresh токена на Canva?
05Какво следва
06Източници
- Canva: Authentication 🌐 глобален — OAuth 2.0 с PKCE, адрес за съгласие, токени.
- Canva: Autofill guide — brand шаблони, dataset, задача за autofill, бележките за плана.
- Canva: Create design autofill job — тяло на заявката, режими, лимит 60/мин.
- Canva: Create design export job — формати, качество, лимити и грешки.
- Canva: Scopes — пълният списък с права.
- Canva: API requests and responses — асинхронни задачи и polling.
- Canva REST APIs — OpenAPI описание — пътища, лимити, наличност по планове.
- Canva MCP: инструменти и лимити — по планове.
- Canva Help: свързване с AI асистент — стъпки за конкретните асистенти.
- n8n: HTTP Request credentials 🔒 — OAuth2, PKCE, Header.
- n8n: променливи за възлите —
NODES_EXCLUDEи Execute Command. - Puppeteer 🔒 — документация за резервния път.