Offer generator (Market integration)

Última actualización: Apr 26, 2026Sección: General

Offer generator (Market integration)

Full API reference: https://api.mailoo.app/docs/v1

The Offer generator is a Market-only feature: wizards are scoped to a MARKET integration so they can resolve published products and price lists from the same catalog. Offer storage, SMTP, and outbound delivery are self-contained (separate tables and mail settings from other integration types).

For catalog read endpoints (types, products, price lists), continue to use ``market.external-read`` as documented in market-catalog-external-api{.interpreted-text role="doc"}.

Overview

  • Dashboard: Project → Market integration → tab Offer generator --- list, create, edit, delete generators; configure wizard JSON, optional AI, and SMTP; external URL patterns for pre-form.
  • Owner API (JWT / session Bearer): Create and update generators and mail settings under /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators….
  • External API (``X-API-Key``): Same path prefix as Market external routes --- /api/v1/market/{projectUid}/integrations/{integrationId}/offer-generators/… --- with separate scopes from market.external-read.
  • Scopes:
  • ``offer-generator.external-read`` --- GET …/offer-generators/{generatorId} (wizard JSON with catalog blocks resolved for published data only).
  • ``offer-generator.submit`` --- POST …/offer-generators/{generatorId}/submissions (validate answers, optional AI, persist offer, send mail).
  • Persistence: OfferGenerator, OfferGeneratorMailSettings, GeneratedOffer, OfferDelivery (Prisma). Nothing is written to messages or integration outboundMail.

Wizard configuration (version 1)

Stored as JSON on ``OfferGenerator.wizardConfig``. The last step must be a ``final`` block.

Step shape

  • id --- stable string id (referenced by ai blocks includeStepIds).
  • visibleWhen (optional) --- show the step when var equals equals or when var is in oneOf (exactly one of the two must be set).
  • block --- discriminated by type:
  • ``text`` --- Optional bodyMarkdown; fields[] with name, optional label, kind (text | textarea | email | phone | boolean | pinGroup), required. For pinGroup, options[] (at least two value / label pairs) is required; one value is stored in the field name variable. Each field may set useMarkdownBody (boolean, default false): when true, markdownBody (markdown) is the field prompt instead of label; prepared wizards include markdownBodyHtml per field when markdownBody is set.
  • ``choice`` --- variable; options[] with value, label, optional setVariables map (client may merge into submitted variables).
  • ``product`` --- productIds[]; showFields; optional showPrice (default true). External GET embeds resolvedProducts.
  • ``priceList`` --- priceListId; optional lineSkuFilter; showFields; optional showPrice. External GET embeds resolvedLines.
  • ``ai`` --- promptTemplate (supports {{varName}} placeholders); optional outputVariable (default aiText); optional includeStepIds for extra JSON context. Requires AI settings on the generator (see below).
  • ``final`` --- customerEmailVariable (must match a submitted variable name); optional submitLabel, bodyMarkdown.

AI settings (optional)

Stored on ``OfferGenerator.aiSettings`` as openai-compatible JSON: baseUrl, model, and encrypted apiKeyEncrypted (same encryption mechanism as SMTP secrets). The API never returns the key; owner GET responses expose hasApiKey only.

Mail settings (required for successful email delivery)

PUT /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}/mail-settings --- host, port, secure, user, from, optional replyTo, optional notificationEmail (BCC-style internal alert), optional smtpPassword (write-only on PUT).

If mail settings are missing or SMTP send fails, the submission still creates a ``GeneratedOffer`` with status ``FAILED`` and diagnostics; ``OfferDelivery`` rows record per-channel attempts (``CUSTOMER`` / ``NOTIFICATION``).

Owner API (Bearer)

  • GET /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators
  • POST /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators
  • GET /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}
  • PUT /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}
  • DELETE /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}
  • GET /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}/mail-settings --- returns { data: null } when no row exists yet; otherwise same non-secret fields as after PUT.
  • PUT /api/v1/projects/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}/mail-settings

POST create body includes key (slug per integration), optional name, status, wizardConfig, optional aiSettings (apiKey is write-only when present).

External API (X-API-Key)

Replace placeholders with your project UID, MARKET integration id, and generator id (CUID).

  • GET {baseUrl}/api/v1/market/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}

    Returns { data: { id, key, name, status, wizard } } where wizard.steps[].block may include resolvedProducts / resolvedLines for catalog blocks.

  • POST {baseUrl}/api/v1/market/{projectUid}/integrations/{integrationId}/offer-generators/{generatorId}/submissions

    Body: { "variables": { … } } --- all answers keyed by field / choice variable names. Response: { data: { id, status, customerEmail } } with status COMPLETED or FAILED.

BFF / pre-form pattern

Keep two restricted keys on the server (never in the browser):

  1. Read key --- offer-generator.external-read --- server loads the wizard once (or caches) and renders UI.
  2. Submit key --- offer-generator.submit --- server posts the final variables JSON after validating on your side.

Catalog lookups from the client still go through your BFF using ``market.external-read`` if you need live product data outside the embedded resolvedProducts snapshot.

  • market-catalog-external-api{.interpreted-text role="doc"} --- product and price list reads.
  • market-catalog-csv{.interpreted-text role="doc"} --- bulk catalog edits in the dashboard.
  • outbound-smtp{.interpreted-text role="doc"} --- not used for offer generator mail (offer generator uses its own SMTP table).