Skip to content
All work

ProductsLive

Keforo Studio

AI video ads, product images, Darija voiceovers and full campaigns for MENA e-commerce, from one pool of credits.

Status
Live
Year
2025
Role
Founder

The creative studio for e-commerce teams in Morocco and the wider MENA market. Describe a product and get video clips, product photos, ad copy in Arabic, French or English, voiceovers in Darija, and complete multi-scene video ads, from one dashboard and one pool of credits.

What it is

  • About a hundred catalogued actions across fal.ai, Replicate, OpenRouter and ElevenLabs, each with its price, plan gate and parameter schema, so a new model is a registry entry rather than a new screen
  • Credits are taken before the work starts and refunded exactly on failure, from a three-bucket ledger the whole organisation shares
  • Long provider calls leave the request path: BullMQ queues, a small worker pool, WebSocket status events, every output copied into the studio’s own storage
  • A campaign pipeline that writes a storyboard, waits for review, generates clips and voiceovers, and composes a 720p ad with FFmpeg

How it works

The rules a user runs into, the life of one generation, the campaign pipeline, the credit system, and why the stack is what it is.

#What it generates

Only what the code runs today. Every line below is live in the product.

  • Video. Text-to-video and image-to-video clips of 4 to 12 seconds: Wan 2.5, Kling 2.6 Pro, Veo 3.1, Seedance 1.5, LTX 2.3, Hailuo-02. Veo and Seedance produce their own synchronised audio; the others produce silent clips that get sound at composition time.
  • Image. Text-to-image, editing, background removal, upscaling, face restoration and product shots: the FLUX family, Recraft v3, Nano Banana 2, Bria.
  • Voice and audio. Text-to-speech in many languages including Moroccan Darija, short music tracks, and speech-to-text: ElevenLabs, CassetteAI, Whisper.
  • Text. Chat assistants and ad copy in Darija, French, Modern Standard Arabic and English: Claude, GPT, Gemini and Mistral, all through one gateway.
  • Campaigns. A complete 30 to 120 second video ad assembled from AI clips and AI voiceovers, with a storyboard you review before anything expensive runs.

Around the studio: projects that group generations, a team with roles that shares one credit balance, and a wallet with card payment through Stripe or bank transfer in dirhams.

#The form comes from the catalogue

Every model is an action in a registry: its provider, its model id, its credit cost, the minimum plan it needs and the parameters it accepts. The studio builds the form from that record.

Switch the action and watch the fields, the price on the button and the plan gate change. Switch your plan to see the gate close. Nothing here is hand-written per model.

#One generation, replayed

The path every request takes, whether it is one image or one scene of a campaign. Credits are taken first, the job is queued, one of three workers claims it, the provider runs, the output is copied into the studio’s own storage, and the browser is told over WebSocket.

Keforo Studio · worker · ai-generation
idle

Kling 2.6 Pro (Text to Video)

video.kling26_t2v · 43 credits

A bottle of argan oil on a wooden table, morning light, slow push-in

Free bucket
12
Subscription bucket
1,540
Top-up bucket
400
Balance
1,952
  1. 1POST /ai/generateaction enabled, plan high enough
  2. 2credits.deduct 4312 free, then 31 subscription
  3. 3queue.add ai-generationdurable job, status pending
  4. 4worker[2].claimone of three workers, status processing
  5. 5fal.subscribe kling-video/v2.6/prothe provider call, up to five minutes
  6. 6storage.import → r2://…/gen_8f2c.mp4copied into the studio’s storage, never hot-linked
  7. 7ws.emit generation.completedthe browser hears it at once

#The rules a user feels

  • You pick an action, not a provider. The catalogue lists actions such as “Kling 2.6 Pro, text to video”. Each knows its provider, model id, cost, plan gate and parameters.
  • The price is on the button. Every action has a flat credit cost, shown before you confirm. No surprise charges after the fact.
  • Credits are taken before the work starts. If the balance is short, the request fails with 402 and nothing is created. A job in the queue is a job that is paid for.
  • Three workers at a time. Provider calls block for up to five minutes, so the pool is deliberately small. It caps how much money can be in flight.
  • Outputs are ours. Provider URLs expire. Every output is downloaded into the studio’s storage and served from there.
  • Failure refunds, exactly. A failed generation refunds to the same buckets it was taken from, using the record of the original deduction. It cannot refund twice.
  • Cancel only before it starts. Once a worker has claimed a generation, the provider call cannot be stopped from outside.
  • Retry is a new generation. There is no silent automatic retry, because every attempt costs real money at the provider.

#The status lifecycle

Drawing the diagram

Four states a user sees, and a fifth for a job cancelled while it was still waiting.

#How a request moves through the system

Drawing the diagram

#A campaign, from product to ad

A campaign turns a product description into a complete 720p video ad in five steps, with two places where a human is in the loop: the storyboard review, and a pause if a voiceover fails.

#The rules inside the pipeline

  • The storyboard is a first draft. The pipeline stops after it. Edit any line, add or delete scenes, reorder them, or rewrite a single scene before anything expensive runs.
  • The storyboard follows ad craft. The first scene hooks, the last is the call to action, narration runs at about 2.5 words per second, and a scene prompt never describes a person speaking, because the video model would generate its own speech and fight the voiceover.
  • One reservation for the whole job. Scenes × (clip + voiceover) + composition + storyboard. Sub-steps carry no cost of their own; what is not used is refunded in one move.
  • Clips can fail, voices cannot. A failed clip is skipped and the campaign continues. A failed voiceover pauses the campaign until you retry it or compose without it.
  • Voiceovers are spaced out. Three seconds apart, with a backoff retry on conflict, because the voice provider rejects overlapping requests on one account.
  • Composition is local and deterministic. FFmpeg normalises every clip to 1280×720 at 24 fps, mixes clip audio at 20% under the voice, and concatenates in storyboard order. No AI, no per-campaign cost.

#Credits, in one pool

Credits are the only currency inside the product. One pool covers video, image, voice, text and campaigns, and it belongs to the organisation, not the person, so a team shares it.

  • Three buckets, one order. Free credits are spent first, then the monthly subscription credits, then purchased top-ups. Each deduction records what came from where, which is what makes an exact refund possible.
  • Monthly credits reset with a carry-over. Each plan carries a percentage of unused subscription credits into the next month. Purchased credits never expire.
  • Premium models cost more credits. A draft image is 1 credit; Wan 2.5 video is 31, Kling 2.6 Pro 43, Veo 3.1 193. Users choose the quality and cost trade-off themselves.
  • Prices track the real cost. The actual provider cost is recorded after every generation, provider prices are re-synced every six hours, and a drift above 10% raises a flag. An admin can change a credit cost at runtime.
  • Two ways to pay. Card through Stripe for subscriptions and top-ups, or bank transfer in dirhams with credit packs and multi-month discounts, because most Moroccan businesses do not pay by card.

Plan prices, monthly allotments and top-up rates are in the note linked below, dated, so this page does not go stale.

#Why this stack

Every choice is anchored in something the code has to do.

  • Bun, not Node. TypeScript end to end with no build step. The same runtime serves the API, runs the workers and the scripts, and installs dependencies. One toolchain, fast restarts.
  • Hono. Small, runs natively on Bun, gets out of the way. Routes are declared as data with their role, validation schema and rate-limit tier, so one declaration feeds the router, the permission check and the generated API collection.
  • MongoDB. A generation stores a copy of the action it was created from, so a later price change cannot touch a job already paid for. Each action carries a JSON schema of its parameters, which is the form you tried above.
  • Redis and BullMQ. A provider call takes ten seconds to five minutes and cannot live inside an HTTP request. BullMQ gives a durable queue, a claim only one worker can win, a concurrency cap, and no automatic retry, because a retry costs money.
  • WebSocket, not polling. The worker waits on the provider and reports itself; the API pushes status events on a socket shared with the HTTP port. No provider callbacks, one code path.
  • One gateway for language models. All chat, copy and storyboard calls go through OpenRouter, so Claude, GPT, Gemini and Mistral are catalogue entries that differ by a slug.
  • fal.ai first. It hosts most of the video and image models behind one API and publishes live pricing, which is what makes per-generation cost tracking possible.
  • Our own storage. Provider links expire. Every output is re-downloaded into S3-compatible storage under the organisation’s prefix and served from there.
  • FFmpeg on the server. Assembling clips and mixing audio is deterministic work. Doing it locally costs nothing per campaign and behaves the same every time.
  • Credits over per-video limits. One pool works across every media type, premium models simply cost more, campaigns are the sum of their parts, and margins are protected per model rather than per plan.

#Not there yet

  • Voice cloning is catalogued but the cloning path is not built; Darija voiceovers use multilingual voices.
  • Product images attached to a campaign do not yet feed the clips as image-to-video inputs.
  • Text overlays and transitions are in the storyboard but not rendered; composed ads are clean cuts.
  • A browser-based UGC editor for trimming and re-cutting is in progress.

#The full note

The long version, with the generation and campaign diagrams, the plan table and the reasoning behind each choice.

Read how Keforo Studio works

Did this land?

Conversation