<deployment-name> with your actual Convex deployment name (e.g. tremendous-jaguar-953).
Authentication
All endpoints (except/smartAnalyzeImage) require a Bearer token that matches the INGEST_API_KEY environment variable set in your Convex Project Settings.
401 Unauthorized response.
POST /ingestExternal
Ingest an external image URL into Pindeck for a specific user. The image is downloaded, processed, and persisted to NextCloud. A Convex image record is then created for the target user. For Discord imports (sourceType: "discord"), the image lands with status: pending and waits for moderation before any AI analysis runs. All other source types trigger AI analysis automatically.
If a record with a matching
externalId or the same imageUrl already exists for the user, the endpoint returns the existing image ID without creating a duplicate.Request body
Convex user ID of the target user. The imported image is assigned to this account.
URL of the image to import. Angle-bracket wrapping (e.g.
<https://...>) is stripped automatically.Human-readable title for the image.
Optional description text.
Array of tag strings.
"original" is always appended automatically.Image category. See the gallery for available category values.
Human-readable source label (e.g. a creator name or platform).
Style reference number extracted from the source message.
Deduplication key. If an image with this ID already exists it is returned immediately without re-importing.
Origin of the image. One of
"upload", "discord", "pinterest", or "ai". Discord imports are held as pending until moderated.Original source URL before any normalization. Stored for reference and used as fallback during backfill.
Convex ID of an
importBatches record to group this image with a batch import.Response
Convex ID of the created (or existing) image record.
Convex user ID the image was assigned to. Echoes back the
userId from the request.Error responses
| Status | Meaning |
|---|---|
400 | Missing imageUrl or userId, or both were empty after normalization |
401 | Bearer token missing or does not match INGEST_API_KEY |
502 | NextCloud persist failed — image could not be downloaded or stored |
Example
POST /discordQueue
Fetch the list of images that are pending moderation for a specific user. Used by the Discord bot to present items for review. Only images withstatus: pending that belong to a Discord lineage are returned. Each item includes resolved lineage root metadata so the bot can display the original source context.
Request body
Convex user ID whose pending queue to fetch.
Maximum number of items to return. Clamped to a minimum of
1 and a maximum of 25.When provided, the response is filtered to only include the item matching this Convex image ID. Useful for checking the status of a specific image.
Response
Array of pending image records.
Echoes back the
userId from the request.Error responses
| Status | Meaning |
|---|---|
400 | Missing or empty userId |
401 | Bearer token missing or does not match INGEST_API_KEY |
Example
POST /discordModerate
Approve, reject, or trigger variation generation for a pending Discord-imported image.- Approve — moves the image to
status: draftand schedules AI (VLM) analysis. - Reject — deletes the image record and cleans up any associated NextCloud storage.
- Generate — schedules variation generation immediately. The image must already be approved (
status: activeordraft) before callinggenerate.
Request body
Convex user ID that owns the image.
Convex image ID to act on.
One of
"approve", "reject", or "generate".Number of variations to generate when
action is "generate". Clamped to 1–12. Defaults to 2.Variation style for
"generate". One of "shot-variation", "action-shot", "b-roll", "coverage", "style-variation", or "subtle-variation". Defaults to "shot-variation".Optional direction passed to the generation prompt (e.g.
"medium close-up, wet street").Output aspect ratio for generated variations. One of
"16:9", "9:16", "1:1", "4:3", or "3:4". Defaults to "16:9".Response
true when the action was applied successfully.Human-readable description of the outcome (e.g.
"Image approved.", "Image rejected and deleted.").Echoes back the
imageId from the request.Echoes back the
userId from the request.Updated
status field of the image record. Not present after a rejection.Updated
aiStatus field of the image record. Not present after a rejection.Error responses
| Status | Meaning |
|---|---|
400 | Missing userId or imageId; invalid action value; image is in the wrong state (e.g. calling generate on a still-pending image) |
401 | Bearer token missing or does not match INGEST_API_KEY |
Examples
Approve an imagePOST /admin/backfillNextcloud
Scan for image records whose URLs are not yet canonical NextCloud URLs and migrate them. Images are either re-uploaded from a source URL or published from an existing NextCloud storage path. UsedryRun: true to inspect what would be migrated before making any changes.
Request body
Maximum number of images to scan and process. Clamped to 1–1000.
When
true, the endpoint reports what it would do without writing any changes.When
true, images that already have a NextCloud storagePath but are missing or have stale derivative variants (preview, small, medium, large) are reprocessed to rebuild those derivatives.Response
Whether the request ran in dry-run mode.
Whether variant refresh was enabled.
The effective limit that was applied.
Total number of image records examined.
Number of images successfully migrated (or that would be migrated in dry-run mode).
Number of images that could not be migrated.
Number of images that were already on canonical NextCloud URLs and required no action.
Per-image result entries (up to 50).
Error responses
| Status | Meaning |
|---|---|
401 | Bearer token missing or does not match INGEST_API_KEY |
Example
POST /smartAnalyzeImage
Schedule async VLM analysis for an already-uploaded image. The endpoint enqueues the analysis job and returns immediately — it does not wait for the analysis to complete. This endpoint is called internally by Pindeck after an image is ingested. It is exposed as an HTTP action for external orchestration (e.g. re-triggering analysis from a script or CI pipeline).This endpoint does not require a Bearer token. It relies on the Convex session context passed in the request body.
Request body
Convex ID of the image record to analyze.
Convex user ID that owns the image.
Convex storage ID of the uploaded file. Either
storageId or imageUrl must be provided.Direct URL of the image to analyze. Required when
storageId is not provided.Current title of the image, passed as context to the VLM.
Current description, passed as context to the VLM.
Existing tags, passed as context to the VLM.
Current category, passed as context to the VLM.
Source label, passed as context to the VLM.
Style reference number, stored on the record after analysis.
If greater than
0, variation generation is scheduled after analysis completes.Variation mode to use if
variationCount is set.Response
true when the analysis job was successfully enqueued.Error responses
| Status | Meaning |
|---|---|
400 | Missing imageId, userId, or both storageId and imageUrl |
500 | Unexpected server error |
Example
Legacy aliases
The following paths are legacy aliases retained for older Discord bot configurations. They map to the same handlers as their canonical equivalents and accept identical request/response payloads.| Legacy path | Canonical path |
|---|---|
POST /discord/queue | POST /discordQueue |
POST /discord/moderation | POST /discordModerate |