.dev
REST API

AI

Every Feeef AI feature under /v1/ai — text, code, HTML landing pages, images, image landing pages, voice and studios. All of it runs on Feeef Cloud.

Every AI endpoint lives under /v1/ai/*. Feeef never calls an AI provider directly: each request runs on Feeef Cloud, which owns models, prompts for Cloud products, provider routing and the exact usage receipt. Feeef adds your store and product data, keeps history, and charges your wallet from the receipt.

AreaEndpointsResult
Catalogmodels, collections/{id}Sync
Raw Cloud accessrequests, streamsAsync job / SSE
Texttext/*Sync draft (nothing saved)
Codecode/*Sync code or page structure
HTML landing pageslanding-pagesAsync job → a ProductLandingPage
Imagesimages, images/sync, image-generations, image-prompt-templatesAsync record, or sync
Image landing pagesimage-landing-pagesAsync → one long poster image
Voicevoiceovers, speech, voices, voice-projectsSync audio
Studiosbrand-assets, post-assets, ui-assets, *-studiosAsync image records

Image landing pages and HTML landing pages are different products. An image landing page is a single tall image (a mobile ad poster). An HTML landing page is a builder page (ProductLandingPage) with sections and settings.

Conventions

Auth and scopes

All routes need a Bearer token except the public reads (image-generations/gallery, image-prompt-templates). Tokens need ai.read for GET and ai.generate for everything else; first-party tokens (*) always pass. Otherwise the API answers:

// 403, WWW-Authenticate: Bearer error="insufficient_scope", scope="ai.generate"
{ "error": { "code": "insufficient_scope", "message": "Explicit ai.generate access is required" } }

Store-scoped calls also check your member permissions on storeId.

Idempotency

Every paid request needs an Idempotency-Key header (1–200 printable ASCII characters; voice calls use a UUID). Persist the key before sending and reuse it on retry:

SituationResponse
First callRuns and is billed once
Same key, same body, finishedThe original status and body, replayed — no new charge
Same key, still running503 ai_workflow_pending — retry later with the same key
Same key, different body409 idempotency_conflict
A paid step is still reconciling503 ai_recovery_required — retry with the same key and body
Missing key400 idempotency_key_required

A replay returns exactly what the first call returned, including success: false bodies: use a new key to try again after a failure. The SDKs manage keys for you (CloudAiWorkflowIntents in Dart, ff.withAiWorkflow(key, …) in JavaScript).

Async results

Long jobs return immediately and finish in the background:

  • Image records (images, image-generations, image-landing-pages, studio assets) create an ImageGeneration with status pending → processing → completed (or failed, reason in error). Follow it on the realtime channels image_generations/{id}, users/{userId}/image_generations or stores/{storeId}/image_generations (created, updated, deleted events), or poll GET /v1/ai/image-generations/{id}.
  • HTML landing pages return a jobId; poll GET /v1/ai/landing-pages/jobs/{jobId}.
  • Raw requests return a job; poll GET /v1/ai/requests/{id}.

Billing

AI is postpaid. When Cloud returns the final receipt (charged_micros, in USD micros), Feeef converts it once — micros / 1,000,000 × exchange rate × retail markup, rounded up to 0.001 DZD — and records one ai_generation transfer from your wallet. There is no hold and no balance pre-check; a wallet can go negative and must be topped up. Failed steps that already ran are billed. Admission limits per user: 4 active jobs and 20 new jobs per minute, otherwise 429 feeef_ai_admission_limit (nothing started, nothing charged).

Responses and errors

  • Text, code and sync image/voice endpoints answer 200 with { "success": true | false, "message", ... }. AI or permission failures are success: false with error.
  • Cloud errors keep their status: { "error": { "code", "message", "requestId" } }.
  • Uploads are limited to 20 MiB per request (413 ai_body_too_large).

Catalog

MethodPathDescription
GET/v1/ai/modelsCloud models you can use, with pricing, request_constraints and feeef_image (image options, or null)
GET/v1/ai/collections/{id}Curated model ladder, cheapest first, e.g. image-landing-page. 404 collection_not_found when not configured

Pass storeId to use the store's Cloud workspace; without it your personal workspace is used. A collection rung is { rank, label, description, model } — show it as a quality slider.

Raw Cloud access

Direct access to any Cloud operation (chat/completions, responses, embeddings, images/*, audio/*, videos, google/*, anthropic/messages) with Feeef billing.

MethodPathDescription
POST/v1/ai/requests{ endpoint, body, storeId? } → 202 job. Multipart files only for videos and `audio/transcriptions
GET/v1/ai/requestsYour last 50 jobs; ?key= finds the job of an idempotency key
GET/v1/ai/requests/{id}Job with result once succeeded
GET/v1/ai/requests/{id}/contentBinary result (audio, image, video)
POST/v1/ai/streamsSame body with stream: true for chat/completions / responses; relays SSE

body.model must come from /v1/ai/models. A job is { id, status, endpoint, cloudRequestId, imageGenerationId, billing: { currency: "DZD", chargedMicros, chargeDzd, settled }, error, result }.

Text

Natural language → a validated draft. Nothing is saved; your app applies the draft. All take storeId, input, optional attachments (image, url, audio, product, store) and modelId.

MethodPathDraftPermission
POST/v1/ai/text/product-draftproduct (`mode: createupdate, productId` for update)
POST/v1/ai/text/shipping-price-draftshippingPrice (shippingPriceId for update)shipping_prices.write
POST/v1/ai/text/order-draftorder (pass the current form as order)orders.write
POST/v1/ai/text/list-filterfilter — flat or filterator; send the client schemaany list read
POST/v1/ai/text/date-presetpreset (date-range recipe); send the client schemaany list read
POST/v1/ai/text/custom-fields-draftfields + message — the complete next list (contract v2: every type, options, visibility rules); pass currentFields to edit, kept ids are preserved. Save it yourself with PATCH /v1/stores/{id}/integrations/customFieldsintegrations.write
curl -X POST "https://api.feeef.org/v1/ai/text/product-draft" \
  -H "Authorization: Bearer $FEEEF_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"storeId":"STORE_ID","input":"Black leather wallet, 2500 DZD, 3 colors"}'

Code

Storefront code and builder structures. See Templates → AI for how results plug into TemplateData.

MethodPathProduces
POST/v1/ai/code/snippetdata: HTML, CSS, JavaScript or Markdown (language, `mode: create
POST/v1/ai/code/custom-componentcode + propsSchema + props (+ slots). Edit with current* fields
POST/v1/ai/code/template-page-editFull page (page, globalPropsPatch) or a scoped proposal (pageId, mode, prompt/attachments)
POST/v1/ai/code/landing-page-template-datadata.pages.landing_page + schema from a landing template (templateId, productId, prompt/attachments)

Template code needs store_templates.write, template_components.write, product_landing_pages.write or pages.write on the store.

HTML landing pages

MethodPathDescription
POST/v1/ai/landing-pages{ storeId, productId, name?, prompt?/attachments? } → { success, jobId }
GET/v1/ai/landing-pages/jobs/{jobId}status: pending, completed (with landingPageId) or failed (with error)

The job writes the page copy and design, then creates the ProductLandingPage and sends a push notification. Poll every ~10 seconds.

Images

MethodPathDescription
POST/v1/ai/imagesImage studio: generate or edit with any Cloud image model. 202 ImageGeneration
POST/v1/ai/images/syncGenerate or edit and wait; returns imageUrl
GET/v1/ai/image-generationsYour records, or a store's with storeId; filter tags
POST/v1/ai/image-generationsPrompt/template-driven generation (multipart). 200 record, pending
GET/v1/ai/image-generations/{id}One record (generatedImageUrl, configs, generations[] history, error)
PATCH/v1/ai/image-generations/{id}Rename, or `status: published
DELETE/v1/ai/image-generations/{id}Soft delete
POST/v1/ai/image-generations/{id}/regenerateRun again on the same record (latest run wins; 409 while running)
GET/v1/ai/image-generations/galleryPublic published images
GET/v1/ai/image-prompt-templates[/{id}]Public prompt presets for the image studio
POST/PATCH/DELETE/v1/ai/image-prompt-templates[/{id}]Platform admins only

POST /v1/ai/images (JSON or multipart) takes model (a Cloud image model), prompt, options (from the model's feeef_image choices: aspect ratio, size, quality, background, format), attachments (image URLs on Feeef storage, store, product), optional imageFile and maskFile (PNG with alpha), id to append to an existing record, and storeId.

POST /v1/ai/image-generations takes prompt, storeId, model, aspectRatio, imageSize (1K/2K/4K), resolution, attachments (image, store, product, audio), imageFile, background, quality, outputFormat, googleSearch, imageSearch.

final gen = await ff.ai.generateImage(/* model, prompt, options … */);
// then listen on users/{userId}/image_generations, or:
final record = await ff.imageGenerations.find(gen['id']);

Image landing pages

One tall product poster image (default 9:32), built by Cloud in two steps: a text model writes the copy deck from your product data and photos, then an image model renders it. The result is an ImageGeneration tagged image_landing_page.

MethodPathDescription
POST/v1/ai/image-landing-pagesStart. { success, id }; follow the image record
POST/v1/ai/image-landing-pages/questionsOptional wizard questions (billed, sync)
GET/v1/ai/image-landing-pages/templatesPlatform templates + the store's own (?storeId=)
POST/v1/ai/image-landing-pages/templatesCreate a store template (?storeId= required)
PATCH/v1/ai/image-landing-pages/templates/{id}Update a store template
DELETE/v1/ai/image-landing-pages/templates/{id}Delete a store template
GET/v1/ai/collections/image-landing-pageImage model ladder for the quality slider

Start — text (the brief), attachments (image, product, store), storeId (its Cloud workspace owns templates and the job), templateId, answers (keyed by question key), imageModel (a ladder rung), aspectRatio, imageSize, background, sectionsCount (2–12), loadProductImage (default true; send false when you pass the photos yourself).

Templates are visual direction plus optional questions:

{
  "name": "Gold Ramadan",
  "description": "Matte gold, white marble, soft light",
  "prompt": "Palette: matte gold on white marble. Headline: {{headline}}.",
  "preview_image_url": "https://storage.feeef.net/…/preview.jpg",
  "fields": [
    { "key": "headline", "type": "text", "label": "Headline" },
    { "key": "mood", "type": "select", "label": "Mood", "options": ["Calm", "Festive", "Luxury"] }
  ]
}

Field types: text, textarea, select, multiselect, toggle, color, number. All answers are optional; {{key}} in the prompt takes the answer and other answers are passed as labelled notes. Platform templates are read-only.

Questions — { storeId?, text?, attachments?, templateId?, locale: ar|fr|en } with an Idempotency-Key → { summary, templateFields, fields, billing: { chargeDzd } }.

final ladder = await ff.ai.collection('image-landing-page', storeId: storeId);
final templates = await ff.ai.imageLandingPages.templates(storeId: storeId);
final res = await ff.actions.generateImageLandingPage(
  text: 'Ramadan offer, free delivery',
  attachments: [ProductAttachment(value: productId, label: 'Wallet')],
  storeId: storeId,
  templateId: templates.first.id,
  imageModel: ladder?.rungs.last.model.id,
);

Voice

MethodPathDescription
POST/v1/ai/voiceoversScript → speech in one call: optional script enhancement, style, two-speaker dialogue. Returns audioUrl
POST/v1/ai/speechVoice studio TTS from blocks (voice, text, style; ≤24 blocks, ≤2 voices). Returns audioUrl
POST/v1/ai/speech/script-editRewrite the blocks with a text model (modelId, prompt, blocks, voices)
GET/v1/ai/speech/script-edit/quoteText models available for script editing
GET/v1/ai/speech/pendingUnfinished paid voice jobs (with retryKey)
POST/v1/ai/speech/retryResume a pending job by id
GET/v1/ai/voicesVoice library: built-in, platform and your custom voices (search, pageToken)
POST/v1/ai/voicesCreate a custom voice: prompted (designed) or replicated (cloned with consent)
POST/v1/ai/voices/manageRename or delete custom voices (ids, operation)
GET/POST/v1/ai/voice-projectsSaved speech drafts (list / create)
GET/PUT/DELETE/v1/ai/voice-projects/{id}One draft; PUT needs the current revision (409 revision_conflict when stale)
GET/v1/ai/voice-projects/{id}/takes/{takeId}/audioAudio of a generated take

Voice calls are synchronous (up to ~170 s). If a call ends while the job is still running you get 503 with the job id — call again with the same Idempotency-Key (a UUID) or use /speech/retry. Passing projectId to /speech records the take in that project.

curl -X POST "https://api.feeef.org/v1/ai/voiceovers" \
  -H "Authorization: Bearer $FEEEF_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"storeId":"STORE_ID","text":"عرض خاص هذا الأسبوع — توصيل مجاني!","voiceName":"Fenrir","enhanceScript":true}'

Studios

Brand, social-post and UI mockup studios. Asset calls return immediately with an ImageGeneration id (follow it like any image record).

MethodPathDescription
POST/v1/ai/brand-assetslogoName, description, colors, assetKind (identity, logo, icon, avatar, graphic_elements, landing), brandStudioId or createStudio
POST/v1/ai/post-assetsname, brief, assetKind (feed, story, square, wide), regionStyle, postStudioId or createStudio
POST/v1/ai/ui-assetsname, brief, assetKind (web, mobile, dashboard, landing, settings), style, uiStudioId or createStudio
GET/POST/v1/ai/{brand,post,ui}-studiosList / create studio projects
GET/PATCH/DELETE/v1/ai/{brand,post,ui}-studios/{id}One project, with primaryImages and generationCounts
GET/v1/ai/{brand,post,ui}-studios/{id}/generationsAssets of one kind
PATCH/v1/ai/{brand,post,ui}-studios/{id}/primariesPick the primary asset of a kind (kind, generationId)

SDK map

AreaDart (ff)JavaScript (ff)
Catalogff.ai.models(), ff.ai.collection(id)ff.ai.models()
Raw Cloudff.ai.submit/submitFile/stream/request/contentff.ai.submit/submitFile/stream/request
Text & codeff.actions.updateProductUsingAi, generateSimpleCode, generateCustomComponentCode, …—
Imagesff.ai.generateImage, ff.imageGenerations.*, ff.actions.editOrGenerateSimpleImageff.ai.generateImage, ff.imageGenerations.*
Image landing pagesff.actions.generateImageLandingPage, ff.ai.imageLandingPages.*—
Voiceff.actions.generateVoiceover—
Studiosff.actions.generateBrandAsset/PostAsset/UiAsset, ff.brandStudios/postStudios/uiStudiossame

The generated API reference lists every field of every route.

On this page