Hiring: Node/Typescript Dev

I’d like to create a workflow for mass production of ads that are Ai generated that connect ChatGPT image generation to my Canva. Other programs involved would be vercel, and supabase

3 個讚

Hi Jake,

The tricky part here is Canva — n8n doesn’t ship a native node for it, so this either needs a custom node built against Canva’s Connect API or a set of HTTP Request nodes handling the OAuth refresh and the async export polling. Either works; which one makes sense depends on how often you’re running it and whether you want it maintainable by someone non-technical later.

The rest is straightforward: image gen writes to Supabase storage, Supabase row triggers the Canva design creation, Vercel serves whatever front end or webhook receiver you need.

One question that changes the build a lot — roughly what volume are you targeting? A few hundred ads a month and a few thousand are different architectures (rate limits and queueing).

Happy to sketch the node structure for you either way.

Hamza Ameer
Automation consultant

itsameehamza203@gmail.com

1 個讚

Hello @jakeb ,
This is a good fit, n8n, ChatGPT image generation, Canva, Vercel, and Supabase are all real, everyday tools for me, including genuine Vercel deployment experience, not just Canva template population.

The part worth planning for at “mass production” scale is staying on-brand across hundreds of variations, that usually means Canva’s Brand Kit and template variables doing the layout/text work while the AI generation stays scoped to structured, constrained prompts rather than open-ended image generation, plus a review gate before anything publishes, since image generation costs add up fast at volume if it runs fully unattended.

Upwork: https://www.upwork.com/freelancers/~01446d60f782215efa
Website: https://www.pathfinderautomationsolutions.com
Email: Taiwo@pathfinderautomationsolutions.com

Happy to hop on a call to scope this out properly.

Taiwo

Hey,

We’ve been assisting multiple marketeers and creators with managing autonomous workflows from development to testing to production with complete version tracking, logging and mapping. Thus, building systems end-to-end which helps them in their mass production journey.

Let’s connect on a quick call.
Meeting Link: https://calendly.com/connect-autoagentify/automation-discovery-call

Hi Jake — for mass-producing on-brand ads, the part that looks hardest (Canva) is actually the most solved now: there’s a Canva-maintained community node for n8n that handles OAuth2 (PKCE) and auto-polls the async export/autofill jobs, so no custom TypeScript node or hand-rolled OAuth is needed.

How I’d build it:

  1. ChatGPT generates the image → saved to Supabase storage.
  2. New Supabase row triggers n8n.
  3. n8n runs Canva Autofill: your brand template + the row’s data (copy, image, CTA) → one exported design per row, automatically.
  4. n8n writes the finished asset URL/status back to Supabase (and can fire a webhook to Vercel if you want a front end).

That keeps it maintainable by a non-technical person later — it’s config, not code.

Two things that decide the build:

  • Where does n8n live? The Canva node needs an instance that allows community nodes (self-hosted, or n8n Cloud with community nodes enabled). If you already have one, I build on it — I don’t take over hosting.
  • Volume for v1? I’d ship a first batch (dozens/low hundreds) end-to-end so you can see it running, then add queueing/rate-limit handling before scaling to thousands.

I can share the exact node map, the Canva scopes you’ll need to enable, and screenshots of a similar n8n build I did (web scraper → AI extraction → structured records in Airtable). What’s your n8n setup and rough v1 volume?

— Danny Avelino

Hi Jake,

I saw your post about the AI ad generation workflow and the project sounds very interesting to me.

My background is mainly in full stack SaaS development, TypeScript, APIs, PostgreSQL and AI integrations. I have built and launched several production products myself, including MyBoatDesk, an AI supported mobile app called Nouravo, and LaunchLint.

I also manage my own deployments and infrastructure with Docker, Coolify and Linux, so I am comfortable working across the whole stack rather than only building individual workflow steps.

Your setup with ChatGPT, Canva, Supabase and Vercel is very close to the kind of systems I already work with. I can also implement the orchestration in n8n and adapt to the specific Canva API and OAuth requirements as part of the project.

I am based in Germany, available remotely and can start quickly.

Some of my work:

https://rd-interactive.de

Happy to hear a bit more about the current setup and what you already have in place.

Best,
Rene

Hi Jakeb,

Ad generation at volume is mostly a plumbing problem — the trick is making it survive rate limits and bad generations without you babysitting it.

How I’d approach it in n8n:

  • The pipeline — a trigger (sheet row, Supabase table, or webhook) feeds copy + prompt into ChatGPT image generation, then the output lands in Canva via their Connect API, auto-filled into your brand template so every ad comes out on-brand.
  • Supabase as the source of truth — each ad row tracks prompt, status, image URL and final Canva link, so you can see what’s queued, what failed, and re-run just the failures instead of the whole batch.
  • Vercel for the small pieces n8n shouldn’t do inline — Node/TypeScript endpoints for the Canva OAuth flow, image post-processing, and any custom logic. Plus a simple dashboard if you want to review/approve ads before they go out.
  • Batching and retries — image APIs throttle and occasionally return junk. Queue with backoff, retry logic and a dead-letter table so one bad prompt doesn’t stall the run.

I work in Node/TypeScript daily and have built custom n8n nodes and self-hosted setups, so if something isn’t possible with the stock nodes I’ll just write it.

A few things that shape the build: roughly how many ads per run or per day are you aiming for? Are you on n8n Cloud or self-hosted? And do the finished ads need to stay editable in Canva, or is an exported PNG/MP4 enough?

Best,
Ivan Ceban

Hey Jakeb,

this sounds like a great fit for what I work on.

I can help you build an automated ad-production workflow around OpenAI image generation, Canva, Supabase and Vercel, turning campaign or product inputs into generated creatives, storing the assets and metadata, and preparing them for your Canva workflow at scale.

I’d first map out your current process and then design the automation so you can generate multiple ad variations with as little manual work as possible.

Do you already have a specific Canva template/workflow you want the generated ads to go into, or are you looking to build the whole process from scratch?

Best regards,
Benjamin Fuchs
benjamin.fuchs@beni.systems
beni.systems

Interested, this is close to what I already build day to day.

One thing worth settling early, because it decides the architecture and the cost. Canva’s Connect API can create designs and upload assets, but programmatic editing of an existing design is limited. So “mass production of ads” usually resolves into one of two shapes:

Autofill a brand template. You build one Canva template with named data fields, then fill it per ad through the API. Fast, visually consistent, and the one I would recommend. It does depend on the autofill endpoint, which is gated to Canva Enterprise, so worth confirming what plan you are on before anyone writes code against it.

Compose the image outside Canva and push the finished asset in. More control, no plan dependency, but you are rebuilding layout yourself and it shows.

With Supabase holding ad copy, variants and generated asset URLs, and Vercel serving a review UI, n8n is the orchestrator: generate copy, generate image, upload, autofill, store the result, queue it for human approval.

I would want that approval step in from day one rather than added later. Fully automated generation is exactly where one bad image reaches a live campaign, and at volume you will not be watching every one.

Two questions that change the estimate a lot: what Canva plan are you on, and roughly what volume per day are you targeting?

Node and TypeScript, Vercel and Postgres are my daily stack. Recent n8n work: GitHub - pratham7711/swiss-quote-agent-demo: n8n AI quoting agent prototype for Swiss plumbing/HVAC: extraction-only LLM, deterministic catalog matching, refuses to guess · GitHub, a quoting agent built so the LLM cannot invent prices or product references. It extracts structure only and a deterministic matcher does the rest, refusing rather than guessing when something is ambiguous.

Interested. Two questions and an offer, keeping it short since you’ll get a lot
of replies.

The part that usually breaks a pipeline like this isn’t the image generation,
it’s Canva. Its API is template-and-autofill based rather than freeform layout,
so the workflow has to target a prepared template with named fields and push the
generated image plus copy into those slots. Worth knowing up front, because it
changes the build: someone has to design the templates before automation can do
anything useful with them.

Two questions:

  1. Do you already have Canva brand templates set up, or is that part of the job?
  2. Volume - dozens of ads a day, or hundreds? It decides whether we queue and
    rate-limit through Supabase or just run it straight.

My background: n8n with an AI decision step in the middle, called over HTTP
Request nodes against the API directly rather than the community LLM nodes, with
a strict JSON schema on every request so a malformed model response can’t reach
the parse step. Full workflow JSON and setup guide here, read it before you reply
to me if you like:

Comfortable in Node/TypeScript, Supabase and Vercel.

How I work: fully async, everything in writing - a written scope before I build,
then a screen recording of it running on your data. Payment: USD to my US account
by ACH or wire, or USDT if that’s easier - whichever is less friction for you.

Tell me the volume and whether templates exist, and I’ll come back with a fixed
price and a date.

One thing not raised yet that decides whether this is buildable at all: the autofill endpoint in Canva’s Connect API requires the acting user to be a member of a Canva Enterprise organisation. Paid plans only get a limited trial while an integration is still in development, not production quota. So having brand templates prepared is necessary but not sufficient.

Worth confirming which plan the Canva account is on before scoping. If it isn’t Enterprise, the layout has to be composited outside Canva and the build looks completely different - cheaper to find that out now than after the templates are designed.

Hi Jake,

I build n8n workflows that connect AI image generation, storage, and design tools, so the pipeline you’re describing is right in my lane.

The key architectural decision is whether to use Canva’s autofill endpoint (requires Enterprise plan) or compose the image outside Canva and push the finished asset in. That choice changes the whole build, so it’s worth confirming your Canva plan before anyone writes code.

My approach:

- ChatGPT generates the image, saved to Supabase storage

- New Supabase row triggers n8n

- n8n runs Canva autofill (or external composition) with your brand template

- Finished asset URL written back to Supabase

- Vercel serves a simple review/approval UI

I’d ship a first batch end-to-end (dozens of ads) so you can see it running, then add queueing and rate-limit handling before scaling to thousands.

Two questions: what Canva plan are you on, and roughly what volume per day are you targeting?

Happy to share a workflow JSON of a similar build I’ve done.

Canva access is the first gate here. Rook Data Tools can deliver a fixed-scope first phase for USD 195: verify the viable Canva API path, define Supabase job and failure states, build one n8n proof flow from prompt to generated image and stored asset, and document the Vercel handoff. Acceptance is one controlled ad record moving end to end with retry and failure state captured. Live publishing is outside this phase.

Relevant public n8n build: Score and log scraped B2B leads with Apify, OpenAI, and Google Sheets | n8n workflow template

Reply with your Canva plan and expected ads per day, and Rook Data Tools will return the written scope before work starts.

Hi @jakeb — one prerequisite decides the architecture before anyone should quote the full build: Canva Autofill is an asynchronous Connect API job, and Canva’s current production access requires the acting user to belong to a Canva Enterprise organization. Other paid plans only get a limited development trial. If you do not have that entitlement, I would not design v1 around production Autofill.

I can start with a **USD 95 fixed, 2-hour-capped, 24-hour qualification milestone**:

  1. confirm the Canva plan/integration entitlement and inspect one test template’s dataset fields;
  2. produce the exact flow for input → OpenAI image → Supabase source of truth → Canva async job/polling → design/status back to Supabase → optional Vercel review UI;
  3. specify idempotency, rate-limit handling, retries, failed-job replay, cost/volume guards, and human approval;
  4. deliver a written go/no-go plus a fixed-price build scope.

Acceptance: a replayable synthetic-input proof if the entitlement is available; otherwise an evidence-backed alternate architecture and no-go reasons. You keep all OAuth authorization and production credentials; I do not need live customer or campaign data for this milestone.

I run a Hong Kong, human-owned / AI-operated studio and work asynchronously in writing. Public scope and safety boundaries: https://resilient-flows-audit.gchtnh7rgv.chatgpt.site/n8n-debugging

If that milestone fits, reply with your Canva plan, rough ads/day, and whether the output must remain editable in Canva or only be exported.

Hi jakeb, We built something very similar for WorkmateAI.

There, we created an AI-powered ad feature that generates ads for tradespeople based on their profile, lets them customize the branding, template and ad text, and then export the finished ads in different formats.

Your workflow follows the same idea. We can use n8n to connect the AI generation with Canva and handle the flow through Supabase/Vercel so you can produce ads in bulk without doing each one manually.

I’m with Hashlogics and this is very close to work we have already delivered. Happy to discuss how you want the ads generated and set up the simplest version first.

Your stack choice is already right, so I’ll skip the debate and give you the build.

The one thing I’d change: treat this as a queue and a worker, not a linear chain. Vercel writes rows, n8n drains them one at a time, Supabase holds both the queue and the finished creatives, Canva does the layout. That way your creative team keeps editing templates inside Canva instead of you maintaining a renderer, and a single failed ad never takes the batch down with it.

Flow: Vercel form → ad_jobs row in Supabase → n8n picks it up → GPT-4.1 writes copy + image prompt → gpt-image-1 renders the visual → Supabase Storage → uploaded to Canva as an asset → Autofill drops it into your brand template → export PNG → back to Supabase Storage → row marked done → Vercel gallery reads it.

Two hard blockers to clear before you write a single node, because they’ll waste a day otherwise:

  1. The Autofill API requires Canva Enterprise. Both the integration developer and the acting user must sit in a Canva Enterprise org. Pro and Teams accounts only get a limited trial while your integration is in development. If you can’t get Enterprise, the Canva leg has to be swapped for a renderer and the rest of this still stands.
  2. Your brand template needs data fields configured through Canva’s Data Autofill app. Run GET /v1/brand-templates/{id}/dataset first and use the exact field names it hands back. Mine assumes AD_IMAGE, HEADLINE, SUBHEAD, CTA — rename them in the Autofill node body to match yours.

Full working build below. Six steps, both workflows complete.


Step 1 — Supabase schema

create extension if not exists "pgcrypto";

create table ad_jobs (
  id                 uuid primary key default gen_random_uuid(),
  brand              text not null,
  product            text not null,
  audience           text not null,
  angle              text not null,
  offer              text,
  brand_template_id  text not null,
  status             text not null default 'queued',   -- queued | done | failed
  headline           text,
  subheadline        text,
  cta                text,
  image_prompt       text,
  design_id          text,
  final_url          text,
  error              text,
  created_at         timestamptz not null default now()
);

create index ad_jobs_status_idx on ad_jobs (status, created_at);

create table canva_tokens (
  id             int primary key,
  access_token   text not null,
  refresh_token  text not null,
  expires_at     timestamptz not null
);

-- seed row 1 with the token pair from your first OAuth authorization code exchange
insert into canva_tokens (id, access_token, refresh_token, expires_at)
values (1, 'PASTE_ACCESS_TOKEN', 'PASTE_REFRESH_TOKEN', now() + interval '4 hours');

insert into storage.buckets (id, name, public)
values ('ad-assets', 'ad-assets', true);

-- RLS on, no policies = service_role only. Do not expose canva_tokens to anon, ever.
alter table ad_jobs      enable row level security;
alter table canva_tokens enable row level security;

Both workflow JSONs below are minified so the whole build fits in one post — copy the block and paste it straight onto the n8n canvas, it expands on import.


Step 2 — Workflow A: Canva token refresher

Import and run this one on its own. It exists because Canva refresh tokens are single-use: every refresh returns a new refresh token and invalidates the old one. If two executions refresh concurrently you get invalid_grant / Refresh token used twice. All access tokens granted from this flow are now revoked and the entire pipeline is dead until you re-authorize by hand. So exactly one workflow ever touches the refresh endpoint, and everything else reads the stored access token out of Supabase.

Access tokens live 4 hours; this runs every 3.

Credential needed: Basic Auth with your Canva client_id as username and client_secret as password.


Step 3 — Workflow B: the ad factory

Batch size 1 on purpose. gpt-image-1 at quality: high returns a large base64 blob and takes 30–60s; fanning out will blow up worker memory and trip Canva’s per-user rate limits at the same time.


Step 4 — Vercel intake route

app/api/ads/route.ts — inserts N variants and pings the n8n webhook.

import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

export async function POST(req: NextRequest) {
  if (req.headers.get("x-api-key") !== process.env.INTAKE_API_KEY) {
    return NextResponse.json({ error: "unauthorized" }, { status: 401 });
  }

  const { brand, product, audience, brand_template_id, variants } = await req.json();
  if (!variants?.length) {
    return NextResponse.json({ error: "no variants" }, { status: 400 });
  }

  const rows = variants.map((v: { angle: string; offer?: string }) => ({
    brand, product, audience, brand_template_id,
    angle: v.angle, offer: v.offer ?? null, status: "queued",
  }));

  const { data, error } = await supabase.from("ad_jobs").insert(rows).select("id");
  if (error) return NextResponse.json({ error: error.message }, { status: 500 });

  // fire-and-forget; the 10-minute schedule trigger is the safety net
  fetch(process.env.N8N_WEBHOOK_URL!, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-intake-secret": process.env.N8N_WEBHOOK_SECRET!,
    },
    body: JSON.stringify({ queued: data.length }),
  }).catch(() => {});

  return NextResponse.json({ queued: data.length, ids: data.map((r) => r.id) });
}

The gallery page just selects id, headline, final_url, status from ad_jobs — public bucket means the URLs render directly, no signing.

Env vars on Vercel: SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, INTAKE_API_KEY, N8N_WEBHOOK_URL, N8N_WEBHOOK_SECRET.


Step 5 — After importing

Find-and-replace YOUR-PROJECT.supabase.co (three places: Archive Raw Image, Store Final Creative, and the final_url field on Mark Job Done), then attach four credentials:

  • REPLACE_SUPABASE_CRED → Supabase API, service_role key
  • REPLACE_SUPABASE_BEARER_CRED → Header Auth, name Authorization, value Bearer <service_role key>
  • REPLACE_OPENAI_CRED → Header Auth, name Authorization, value Bearer <openai key>
  • REPLACE_CANVA_BASIC_CRED → Basic Auth, Canva client id/secret (Workflow A only)

Canva scopes on the integration: asset:write, brandtemplate:content:read, brandtemplate:meta:read, design:content:write, design:meta:read.


Step 6 — What will bite you at volume

Canva export quotas are per-user, and they’re the real ceiling. 75 exports per 5 minutes and 500 per 24 hours for each user your integration acts on behalf of; 750 per 5 min and 5,000 per 24h for the integration overall. So one connected Canva account caps you at ~500 finished ads/day no matter how fast the rest runs. If you need more, store several rows in canva_tokens (one per connected Canva user) and round-robin the token in the Get Canva Token node.

Export URLs die in 24 hours. That’s why the workflow downloads and re-hosts immediately rather than writing Canva’s URL into the database. Don’t shortcut this — you’ll ship a gallery that’s blank the next morning.

If n8n overrides your Content-Type on the asset upload (binary body mode sometimes forces the file’s own mime type over the application/octet-stream header Canva requires), swap the Upload Asset To Canva node to the URL variant instead — you already have the file in a public bucket:

POST https://api.canva.com/rest/v1/url-asset-uploads
Content-Type: application/json

{ "name": "ad-{{job_id}}.png",
  "url": "https://YOUR-PROJECT.supabase.co/storage/v1/object/public/ad-assets/raw/ad-{{job_id}}.png" }

Then poll GET /v1/url-asset-uploads/{jobId} instead. Note it’s a preview endpoint, and it 400s with Asset Upload for url {url} already exists if you resubmit the same URL — the per-job filename in the workflow already keeps them unique.

Pin the image prompt away from text. gpt-image-1 will happily render garbled words into the image, which then collide with your template’s real headline. The system prompt above explicitly bans text and reserves negative space in the upper third; keep that constraint if you rewrite it.

Autofill only fills text and image fields. Colours, fonts, and sizes come from the template. If the client wants per-ad colour changes, that’s a separate template per palette, not an API parameter.

Hit this on a client build — the gotcha is Canva’s single-use refresh token: any second concurrent refresh revokes the entire token lineage and silently kills every downstream execution. Add an error trigger on that node if it’s going to production.

Jake — the first thing I would verify is whether your Canva plan and app have production access to Autofill / Connect API. That determines whether the workflow can keep each result editable in Canva or needs an export-and-review fallback.

I can build a bounded USD 450 pilot in four business days:

  • one Supabase ad record triggers n8n
  • OpenAI generates the image from structured fields
  • n8n sends it through the available Canva path
  • status and output are written back to Supabase
  • a simple Vercel review screen prevents automatic publishing

Acceptance: 10 synthetic records complete exactly once, or fail into a visible retryable state; no ad publishes automatically. I’ll include the workflow export, environment-variable map, and handover notes. This excludes provider and Canva fees, bulk migration, and production ad spend.

Which Canva plan are you using, about how many ads per day do you expect, and must the final asset remain editable in Canva?

Arda — AI Firm

Hi — the first gate I would verify is whether your Canva app has production access to the Connect API path you need. That decides whether the result can remain editable in Canva or needs a controlled export-and-review fallback.

I can take a bounded first phase: one Supabase job enters the queue, OpenAI produces one image, n8n sends it through the viable Canva path, and every status or failure is written back with a retry-safe key. Acceptance would be one reproducible ad record plus the documented fallback before anyone quotes the full build.

Relevant proof (synthetic lab, not client work): n8nポートフォリオ|Webhook・API・再試行・重複防止|AUTOMATION NOTE It shows retries, idempotency, explicit failure paths, and evidence-based acceptance on n8n 2.33.7.

If this is still open, which Canva plan/app access do you have, and do you already have one editable template with named fields?

Hi Jake — the stack makes sense. The first risk I’d de-risk is Canva access: Connect/Autofill eligibility and whether the account plan supports the production path. I’d start with one synthetic ad record: structured brief → OpenAI image → Supabase status/storage → Canva path (or a documented fallback) → Vercel review, with idempotent job state, retries, and a human approval before publishing. That makes the Canva-plan constraint visible before anyone builds the full batch system. If this is still open, what Canva plan or app access do you have, roughly how many ads per day, and must the final assets stay editable? I can scope a small fixed-price qualification phase; no production credentials are needed for that phase.

Hi Jakeb,

I’d be interested in helping with this.

I work with n8n and AI automation, including API integrations and workflows that connect AI services with external platforms and databases.

For a mass ad-production workflow, I’d be interested in understanding how you want the process structured from generation through to Canva and final asset management.

A couple of questions:

• Are the ad concepts/copy also being generated by AI, or are you starting with predefined content?

• Do you already have a Canva template system you want the workflow to populate?

• What role are you expecting Vercel and Supabase to play in the workflow?

Happy to discuss the architecture and see what would be the best way to structure it.