Знакът на КАГАМИ КАГАМИ
kagami.bg/academy · lesson · machine-readable viewVERIFIED 2026-10-01 · UPDATED 2026-10-01
IDENTITY
module
DevStation-04 · Canva Connect API and n8n: automated designs
series
DevStation · lesson 4 of 6
level
Intermediate
duration
1–2 h
prerequisites
A running n8n instance (lesson 3); a Canva account with MFA enabled; for autofill, a Canva plan with brand templates (see KEY CONCEPTS)
trust_label
VERIFIED 2026-10-01 (endpoints, scopes, OAuth flow, rate limits and plan notes checked against canva.dev and the public OpenAPI description) · UPDATED 2026-10-01 · NOT run end-to-end against a live Canva account
language
human view: bg · english edition: /en/academy/devstation/ (same file name)
previous
DevStation-03 · Docker, n8n, OpenClaw
next
DevStation-05 · Marketing AI agent
PURPOSE

Generate on-brand visuals programmatically: publish a Canva brand template with named data fields, call the Canva Connect REST API from n8n (OAuth 2.0 authorization code flow with PKCE), autofill the template with generated text and uploaded images, poll the asynchronous jobs, export the finished design as PNG or PDF and save the file. Includes rate-limit planning for batches, the Canva MCP server as an alternative for AI assistants, and a plan-independent fallback (HTML to PNG with a headless browser).

KEY CONCEPTS
COMMANDS / PATHS
CHECKLIST
NEXT MODULE

DevStation-05 · Marketing AI agent (idea → copy → Canva design → publishing time) · offer: Quick experiment (kagami.bg/stalbata/)

SOURCES
TAGS
canvaconnect-apioauth2pkceautofillbrand-templatesn8nrate-limitsmcp
ПРОВЕРЕНО · 01.10.2026 ОБНОВЕНО · 01.10.2026

Canva Connect API и n8n: автоматизирани дизайни

Един шаблон, хиляда варианта. Подготвяш дизайн в Canva, а n8n го попълва с текст и снимки, изчаква Canva да го сглоби, изнася го като PNG и го записва. Тук минаваме целия път: влизането с OAuth 2.0, шаблоните, заявките, лимитите и честния въпрос „кой план ми трябва“.

⏱ 1–2 ч Средно DevStation · Обучение 4/6 Canva · OAuth · autofill · n8n
n8n (работният процес)🔒 локално Canva Connect API🌐 глобален Canva MCP (за AI асистенти)🌐 глобален Puppeteer (резервен път)🔒 локално
🔄
ОБНОВЕНО · 01.10.2026 — какво
Уроците от 2024 г. имаха няколко неверни неща и са поправени по официалната документация на Canva (canva.dev) и публичното ѝ OpenAPI описание. Endpoint-ите: autofill е 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Какво ще научиш

02Преди да започнеш

Какво искашКакво пише в документацията на 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 и нагоре“.
⚠️
Не залагай цял процес на план, който не си проверил
Преди да построиш десет работни процеса около autofill, направи ръчно една заявка до /brand-templates със своя токен. Ако получиш 403 с текст за липсваща възможност (capability), планът ти не го включва — тогава ползвай резервния път в края на урока.

03Стъпки

  1. Картата на пътя

    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] → файл
  2. Създай приложение в 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 адрес, който контролираш, и махни локалните адреси, преди да подаваш публично приложение.

  3. Как работи входът: 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 на работен процес.
  4. Свържи 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.
  5. Подготви 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.

  6. Работният процес: 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 — иначе файлът остава вътре в контейнера и го губиш при рестарт.

  7. Изчакването: polling с backoff

    Canva препоръчва експоненциално нарастващ интервал: започваш бързо, удължаваш паузата до таван. Така не удряш лимитите (проверката на export е 120 в минута) и не висиш излишно. Цикълът в n8n: Code → Wait → HTTP (проверка) → If (success?). При „не“ — връщаш към Code. При failed или достигнат таван — стоп със грешка.

    n8n · Code възел „Backoff“ · Run Once for Each Item
    const 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.
  8. Масово генериране и лимитите

    Десет продукта — десет дизайна. Вземаш редове от 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 на ден за потребител. При хиляди — раздели на дни или потребители.

  9. 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 — за повтарящ се, неуправляван от човек процес.

  10. Без подходящ план: HTML → PNG

    Ако autofill не ти е достъпен, остава път, който не зависи от Canva: HTML шаблон + безглав браузър. По-беден е визуално, но е безплатен и 🔒 напълно локален. Пример с Puppeteer (изисква Node.js 22 или по-нов):

    bash + Node.js · html-to-image.js
    mkdir -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Проверка

Чеклист

Тест

1. Какъв вход ползва Canva Connect API?

2. POST /autofills върна in_progress. Какво правиш?

3. Колко заявки POST /exports може да прави един потребител на минута?

4. Какво е вярно за refresh токена на Canva?

05Какво следва

06Източници

  1. Canva: Authentication 🌐 глобален — OAuth 2.0 с PKCE, адрес за съгласие, токени.
  2. Canva: Autofill guide — brand шаблони, dataset, задача за autofill, бележките за плана.
  3. Canva: Create design autofill job — тяло на заявката, режими, лимит 60/мин.
  4. Canva: Create design export job — формати, качество, лимити и грешки.
  5. Canva: Scopes — пълният списък с права.
  6. Canva: API requests and responses — асинхронни задачи и polling.
  7. Canva REST APIs — OpenAPI описание — пътища, лимити, наличност по планове.
  8. Canva MCP: инструменти и лимити — по планове.
  9. Canva Help: свързване с AI асистент — стъпки за конкретните асистенти.
  10. n8n: HTTP Request credentials 🔒 — OAuth2, PKCE, Header.
  11. n8n: променливи за възлите — NODES_EXCLUDE и Execute Command.
  12. Puppeteer 🔒 — документация за резервния път.