Skip to main content
Build a full-stack image generation flow with Convex: start tasks from a Convex action, persist rows in the Convex database, update them from a signed Mynth webhook, and render results with reactive queries. Use this when you want durable backend state and live UI updates without polling. For a plain Node server or scripts, use the JavaScript SDK directly. For the webhook helper API alone, see Convex integration.

Architecture

Before you start

You need:
  • a Convex project with auth configured (ctx.auth.getUserIdentity())
  • a Mynth API key
  • a dashboard webhook (created in a later step)
Install the SDK where your Convex functions run:
This guide uses the Convex split:
  • actions for third-party API calls (generateAsync)
  • HTTP actions for incoming webhooks
  • queries for reactive UI

1. Environment variables

Set these on your Convex deployment: Your webhook URL will be your Convex HTTP action, for example:

2. Schema

One images table is enough for a first integration:
This supports one prompt generating several images and a reactive gallery keyed by Mynth task ID.

3. Database helpers

Put queries and mutations in convex/images.ts:
listByMynthTaskId is public and scoped to the signed-in user. getByMynthTaskId is internal for the webhook handler.

4. Start generation from a Convex action

Create convex/imagesActions.ts. Pattern:
  1. Call mynth.image.generateAsync() (returns a TaskAsync immediately).
  2. Create one pending row per expected image, keyed by task.id.
  3. Return mynthTaskId so the UI can subscribe.
Create pending rows after generateAsync succeeds so a failed API call does not leave orphan rows. With a dashboard-managed webhook (next steps), you do not pass webhook on each request.

5. Handle signed webhooks in convex/http.ts

@mynthio/sdk/convex provides mynthWebhookAction(), which:
  • verifies X-Mynth-Signature (HMAC-SHA256) using MYNTH_WEBHOOK_SECRET
  • requires X-Mynth-Event
  • routes on payload.event to your handlers
  • passes Convex context so you can run queries and mutations
imageTaskCompleted can still include per-image failures (image.status === "failed" with error.code). imageTaskFailed means the whole task failed — there is no result payload. Pass { webhookSecret: "..." } as the second argument to mynthWebhookAction if you prefer not to use the env var.

6. Create the webhook in the Mynth dashboard

Use the dashboard webhooks page:
1

Set the URL

2

Choose events

Subscribe to:
  • task.image.generate.completed
  • task.image.generate.failed
3

Store the secret

Copy the webhook secret into Convex as MYNTH_WEBHOOK_SECRET.
With a dashboard-managed webhook, do not attach a webhook object on each generateAsync(...) call for this flow.

7. Render results in React

Convex queries are reactive. After the action returns a task ID, subscribe with useQuery; the UI updates when the webhook patches rows.
Loop summary:
  1. Action starts async work and returns mynthTaskId.
  2. UI subscribes by that ID.
  3. Webhook updates rows (success or failure per image, or whole-task failure).
  4. Convex pushes the latest state into the UI.

Map webhook results to rows

Option 1: Match by mynthTaskId and array order

Used above. Create pending rows when the task starts, look them up with getByMynthTaskId, and match payload.result.images[index] to rows[index]. Best for prototypes and one task with a known image count.

Option 2: Put Convex row IDs in metadata

Create rows first, send their IDs in metadata, attach mynthTaskId after the API call, then update by ID in the webhook from payload.request.metadata:
In the completed handler, read payload.request.metadata.imageRowIds and patch each ID directly instead of looking up by task and index. Use this when you need deterministic row-level mapping. If generateAsync fails after rows were created, clean up or mark those rows failed yourself. Metadata is returned on the task and in webhook payloads. Max size: 2 KB — see Use Metadata.

Request-level custom webhooks

Dashboard-managed webhooks are the default for this Convex pattern. Use request-level custom webhooks only when you need a different destination per request, a tenant-specific URL, or a temporary endpoint:
Request-level custom webhooks are not signed. mynthWebhookAction() expects signed dashboard deliveries (X-Mynth-Signature + MYNTH_WEBHOOK_SECRET). For custom URLs, add your own verification token in the path or query string and handle the raw payload yourself.
Details: Use Webhooks.

Next steps

Convex integration

mynthWebhookAction handlers and event map.

Use Webhooks

Delivery model, signatures, and event payloads.

Batch Generation

Multi-model and multi-prompt generation patterns.

Tasks and Polling

Compare webhooks with SDK task polling.