14  Datasets

27 operations.

14.0.1 getCocoAutoMapperInfo

GET /api/coco-auto-mapper

Shows usage instructions for the annotation-import tool below.

Why it exists. † Describe how the COCO import expects to be called. A self-documenting response for an endpoint driven by hand rather than by the interface.

Unauthenticated — returns static self-documentation (usage, options, examples) for the COCO auto-mapper endpoint.

Authentication: none

Returns — message · description · usage · options · examples

Field names as the handler returns them; the source states no types for them.

Responses

Status Meaning Body
200 Usage documentation.

14.0.2 mapCocoAnnotations

POST /api/coco-auto-mapper

Imports externally produced (COCO-format) annotations onto a dataset’s images by matching filenames.

Why it exists. † Match a COCO annotation file’s images to a dataset’s images by filename and import the annotations against them. Runs in analyse mode to report what it would match, or execute mode to write it.

Analyzes (default) or executes mapping of COCO images/annotations onto an existing dataset’s images, matching by exact filename and/or DJI-filename timestamp. Execute mode batch-inserts into coco_image_mappings and annotations with de-duplication; the caller must have RLS access to the dataset (404 otherwise), while the mapping itself runs with service-role privileges. Execute-mode failures are returned as 200 with success:false.

Authentication: CookieAuth, SupabaseAuth

Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.

Effect — writes (insert) annotation_categories · annotations · coco_image_mappings · datasets · images

Request body

Field Type Required
datasetId string yes
mode string — one of analyze, execute no
mappingStrategy string — one of exact, timestamp, all no
timestampPattern string no
batchSize integer no
cocoData object no
cocoFilePath string no

Responses

Status Meaning Body
200 Analysis report (mode=analyze) or execution stats (mode=execute; failures also return 200 with success:false).
400 Missing datasetId or invalid mode. Error
401 Unauthorized. Error
404 Image or dataset not found, or outside the caller’s organizations. Error
500 COCO load failure or database error. Error

14.0.3 listDatasets

GET /api/datasets

Lists your datasets, optionally just those in one organization.

Why it exists. ‡ List the campaign datasets the caller may see, each carrying its image count and whether this caller may edit it, so the dataset browser can render the grid and disable controls without a second round trip.

Called by — app/(authenticated)/w/data-explorer/anomalies/map/page.tsx · components/create-note-dialog.tsx

Reached from — /w/data-explorer/anomalies/map · /w/data-explorer/insights

Retrieves all datasets for the current user, optionally filtered by organization.

Authentication: SupabaseAuth

Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.

Effect — reads datasets · images · organization_members · organizations

Parameters

Name In Required Type Notes
organization_id query no string (uuid)

Responses

Status Meaning Body
200 List of datasets Dataset
400 Invalid organization_id. Error
401 Not authenticated Error
500 Server error Error

14.0.4 createDataset

POST /api/datasets

Creates a new dataset.

Why it exists. † Register a dataset that already exists in a customer’s own object storage, recording where it lives and the credentials to reach it. Accepts a bearer token because it was built for callers outside the browser.

Creates a new dataset with the specified configuration.

Authentication: SupabaseAuth

Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.

Effect — writes (insert) datasets

Request body

Field Type Required
name string yes
description string no
organization_id string (uuid) yes
visibility string — one of private, organization no
provider string — one of aws, azure, gcp yes
storage_path string yes
credentials object no

Responses

Status Meaning Body
201 Dataset created successfully Dataset
400 Invalid request Error
401 Not authenticated Error
500 Server error Error

14.0.5 createDatasetAsAdmin

POST /api/datasets/create

Creates a dataset in one of your organizations (membership enforced by the database’s row rules).

Why it exists. † Insert a dataset row directly, bypassing row-level security. A minimal creation path kept for internal and scripted use; the tenant checks the ordinary create path performs are absent here.

Called by — app/datasets/create/page.tsx

Reached from — /datasets/create

Inserts the request body via the caller’s RLS-scoped client — the datasets INSERT policy requires ownership or membership of the target organization (403 otherwise). owner_id defaults to the caller; fields are not otherwise validated.

Authentication: CookieAuth, SupabaseAuth

Returns — data

Field names as the handler returns them; the source states no types for them.

Effect — writes (insert) datasets

Request body

Field Type Required
name string no
description string no
organization_id string no
provider string no
storage_path string no
credentials object no

Responses

Status Meaning Body
200 Dataset created Dataset
400 Bad request Error
401 Unauthorized. Error
403 Forbidden — not a member of the target organization. Error
500 Server error Error

14.0.6 previewOnboardingBucket

POST /api/datasets/list-bucket

Previews the files inside an Azure storage container during onboarding (first 200 items).

Why it exists. ‡ List the containers available under a set of storage credentials, so the onboarding wizard can offer the customer a choice rather than asking them to type a container name.

Called by — modules/dataset-onboarding/services/onboarding-api.ts

Reached from — /w/data-explorer/new-dataset

Lists up to 200 media/data objects under an optional prefix in a caller-supplied Azure container, for the onboarding wizard preview. Credentials come from the request body and are not persisted. Azure only.

Authentication: CookieAuth

Returns — ok · files · truncated

Field names as the handler returns them; the source states no types for them.

Request body

Field Type Required
provider string — one of azure yes
accountName string yes
accountKey string yes
containerName string yes
prefix string no
maxFiles no

Responses

Status Meaning Body
200 Matching files (filtered to media/data extensions).
400 Missing storage fields. Error
401 Unauthorized. Error
422 Unsupported provider (only Azure). Error
500 Listing failed. Error

14.0.7 createDatasetFromOnboarding

POST /api/datasets/onboarding

Creates a dataset from the Data Explorer onboarding wizard, storing its Azure connection details.

Why it exists. ‡ Create a dataset from the storage-connection wizard once its credentials have been validated, so a customer can bring an existing image container into the platform in one step.

Called by — modules/dataset-onboarding/services/onboarding-api.ts

Reached from — /w/data-explorer/new-dataset

Creates a dataset row with Azure credentials embedded, for the Data Explorer onboarding wizard. Requires membership of the target organization. Azure only; indexing is queued, not performed inline.

Authentication: CookieAuth

Returns — ok · dataset

Field names as the handler returns them; the source states no types for them.

Effect — writes (insert) datasets

Request body

Field Type Required
name string yes
description string no
organization_id string (uuid) yes
provider string — one of azure yes
account_name string yes
container_name string yes
account_key string yes
visibility string — one of private, organization no
path_prefix string no

Responses

Status Meaning Body
200 Created dataset (returned with status 200).
400 Missing required fields. Error
401 Unauthorized. Error
403 Not a member of this organization. Error
422 Unsupported provider (only Azure Blob). Error
500 Failed to create dataset. Error

14.0.8 updateDatasetAsAdmin

POST /api/datasets/update

Updates a dataset’s settings (dataset editor) or its saved 3D camera position (3D viewer), dispatching on request content type. Requires a session; the caller must own the dataset or hold an admin/owner role in its organization.

Why it exists. ‡ Update a dataset’s stored details from the dataset editor, including re- pointing it at different storage.

Called by — app/(authenticated)/datasets/[slug]/settings/dataset-settings-form.tsx · modules/photo-map-3d/core/hooks/use-3d-asset-handlers.ts

Reached from — /datasets/<slug> · /datasets/<slug>/settings · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Dispatches on request Content-Type: multipart/form-data updates the dataset editor’s settings (name, description, visibility, regenerating the slug from a new name) and redirects to the dataset’s page on success; application/json updates only the 3D viewer’s saved camera position. Runs as the caller under RLS — the caller must own the dataset or hold an admin/owner role in its organization, not a service-role bypass.

Authentication: CookieAuth

Returns — message · data

Field names as the handler returns them; the source states no types for them.

Effect — writes (update) datasets · organization_members

Request body

Field Type Required
slug string yes
default_camera object no

Responses

Status Meaning Body
200 Camera updated (application/json mode); data is present only when a change was applied. Dataset
307 Settings updated (multipart/form-data mode) — redirects to the dataset’s page; empty body.
400 Bad request Error
401 Unauthorized. Error
403 You don’t have permission to edit this dataset. Error
404 Dataset not found Error
500 Server error Error

14.0.9 verifyOnboardingStorage

POST /api/datasets/verify-storage

Checks that the Azure storage credentials entered in the onboarding wizard actually work, without saving them.

Why it exists. ‡ Check that a set of storage credentials actually works before a dataset is created against them, so the wizard fails at the point the operator can still fix it.

Called by — modules/dataset-onboarding/services/onboarding-api.ts

Reached from — /w/data-explorer/new-dataset

Validates Azure Blob account-key credentials and container read/list access with live Azure calls. Nothing is persisted; credential or network failures return 400, not 500.

Authentication: CookieAuth

Returns — ok · displayUrl · canList

Field names as the handler returns them; the source states no types for them.

Request body

Field Type Required
provider string — one of azure yes
accountName string yes
accountKey string yes
containerName string yes

Responses

Status Meaning Body
200 Credentials valid and container listable.
400 Missing fields, or verification failed (bad credentials/network). Error
401 Unauthorized. Error
403 Could not list blobs (missing read permission). Error
404 Container not found. Error
422 Unsupported provider (only Azure Blob). Error

14.0.10 getDataset

GET /api/datasets/{slug}

Shows one dataset’s details.

Why it exists. ‡ Fetch one dataset by its slug, together with the name of the organisation that owns it, for the dataset detail page header.

Called by — hooks/useDatasetName.ts · lib/services/thumbnail-service.ts

Reached from — /w/data-explorer/assets · /w/data-explorer/categories · /w/data-explorer/new-dataset · /w/data-explorer/storage · /w/integrity/actions · /w/integrity/assets …and 7 more

Retrieves details for a specific dataset by slug.

Authentication: SupabaseAuth

Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.

Effect — reads datasets · organizations

Parameters

Name In Required Type Notes
slug path yes string

Responses

Status Meaning Body
200 Dataset details Dataset
400 Dataset slug is required Error
401 Not authenticated Error
404 Dataset not found Error
500 Server error Error

14.0.11 updateDataset

PATCH /api/datasets/{slug}

Edits a dataset’s name, description, visibility, or cover image.

Why it exists. ‡ Rename a dataset, change its description or visibility, or set its cover image, from the dataset settings panel.

Called by — components/notes/components/promote-note-dialog.tsx · modules/gallery/overlays/components/image-viewer.tsx

Reached from — /datasets/<slug> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/data-explorer/insights · /w/integrity/assets · /w/integrity/evidence-explorer

Updates dataset fields (name, description, visibility, cover image).

Authentication: SupabaseAuth

Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.

Effect — writes (update) datasets · organizations

Parameters

Name In Required Type Notes
slug path yes string

Request body

Field Type Required
name string no
description string no
visibility string — one of private, organization no
cover_image_id string (uuid) no

Responses

Status Meaning Body
200 Updated dataset Dataset
400 Bad request Error
401 Not authenticated Error
403 Access denied Error
404 Dataset not found Error
500 Server error Error

14.0.12 deleteDataset

DELETE /api/datasets/{slug}

Deletes a dataset and its image records.

Why it exists. ‡ Delete a dataset and the images, annotations, notes and groups hanging off it. Restricted to the dataset’s owner or an administrator of its organisation, since it is not recoverable.

Called by — components/dashboard-client-wrapper.tsx · components/dataset-table.tsx

Reached from — /datasets/org/<id>

Deletes a dataset and its associated images (by slug).

Authentication: SupabaseAuth

Returns — message

Field names as the handler returns them; the source states no types for them.

Effect — writes (delete, update) annotations · coco_image_mappings · data_groups · datasets · images · location_notes · organization_members

Parameters

Name In Required Type Notes
slug path yes string

Responses

Status Meaning Body
200 Dataset deleted successfully
400 Dataset slug is required Error
401 Not authenticated Error
403 Access denied Error
404 Dataset not found Error
500 Server error Error

14.0.13 getDataset3dConfig

GET /api/datasets/{slug}/3d-config

Fetches the dataset’s saved 3D scene setup (models, camera position).

Why it exists. ‡ Return the saved camera position, layer visibility and display settings for a dataset’s 3D scene, so the viewer reopens where the user left it.

Called by — modules/photo-map-3d/core/hooks/use-camera-initialization.ts · modules/photo-map-3d/services/use-supabase-3d-gaussian-splats.ts

Reached from — /datasets/<slug> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Returns the stored 3D scene configuration (gaussian splats, 3D tilesets, default camera, loading message) for a dataset.

Authentication: CookieAuth, SupabaseAuth

Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.

Parameters

Name In Required Type Notes
slug path yes string

Responses

Status Meaning Body
200 3D configuration.
400 Dataset slug is required Error
401 Not authenticated. Error
404 Dataset not found or no 3D configuration. Error
500 Internal server error. Error

14.0.14 replaceDataset3dConfig

PUT /api/datasets/{slug}/3d-config

Saves the dataset’s 3D scene setup.

Why it exists. ‡ Save the 3D scene’s camera position and display settings for a dataset, so a view worth returning to can be preserved.

Called by — modules/photo-map-3d/core/photo-map-3d.tsx

Reached from — /datasets/<slug> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Validates and overwrites the dataset’s 3D configuration. All four fields are written on every update; any authenticated user may currently update any dataset’s 3D config (no ownership check).

Authentication: CookieAuth, SupabaseAuth

Returns — success

Field names as the handler returns them; the source states no types for them.

Parameters

Name In Required Type Notes
slug path yes string

Request body

Field Type Required
gaussian_splats array of object no
3d_tilesets array of object no
default_camera object no
loading_message string no

Responses

Status Meaning Body
200 Updated.
400 Invalid configuration (details lists per-field validation errors) or update failure. Error
401 Not authenticated. Error
500 Internal server error. Error

14.0.15 listDatasetAnnotationCategories

GET /api/datasets/{slug}/categories

Summarizes which annotation categories appear in the dataset, with counts and sample thumbnails.

Why it exists. ‡ Summarise which annotation categories occur in a dataset and how often, with one sample image each, so the gallery can offer a category filter that shows what it will find.

Called by — modules/gallery/core/dataset-images-with-folders.tsx

Reached from — /datasets/<slug> · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Aggregates annotation categories present in a dataset’s images, with per-category image counts and one sample thumbnail, sorted by image count descending. Requires membership of the dataset’s organization.

Authentication: CookieAuth, SupabaseAuth

Returns — categories

Field names as the handler returns them; the source states no types for them.

Effect — reads annotation_categories · annotations · datasets · images

Parameters

Name In Required Type Notes
slug path yes string

Responses

Status Meaning Body
200 Category aggregation.
401 Unauthorized. Error
403 Forbidden — not a member of the dataset’s organization. Error
404 Dataset not found. Error
500 Failed to load categories. Error

14.0.16 searchImagesByContent

POST /api/datasets/{slug}/find-by-content

Finds which of a dataset’s photographs a given picture is — the frame a slide or a report cut it from — by perceptual hash, nearest first, with the Hamming distance stated so a near miss is not read as a hit.

Called by — app/api/mcp/embed/find-by-content/route.ts

Takes a picture (a data: URL or base64 body) and returns the dataset’s images nearest to it by 64-bit DCT perceptual hash (images.metadata.phash, derived when a thumbnail is generated), nearest first, each with its Hamming distance. A re-encoded, resized or mildly recoloured copy of a frame lands within a few bits; an unrelated photograph of the same plant sits around 30; a substantial crop or a rotation is not matched — this answers “which file is this”, not “what shows the same thing”. The response says how many of the dataset’s images carry a hash at all (indexed of total), so an empty answer over an unindexed dataset is not mistaken for absence. Runs under the caller’s RLS: a dataset they cannot see 404s like a missing one.

Authentication: SupabaseAuth

Returns — query_phash · indexed · total · max_distance · matches

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · images

Parameters

Name In Required Type Notes
slug path yes string

Request body

Field Type Required
image string yes
max_distance integer, nullable no
limit integer no

Responses

Status Meaning Body
200 The nearest images, nearest first, within max_distance.
400 The image could not be decoded, or is over 25 MB. Error
401 Not authenticated. Error
404 Dataset not found (or not visible to the caller). Error
500 Server error. Error

14.0.17 listDatasetGeodata

GET /api/datasets/{slug}/geodata

Same GPS listing via the newer route the 3D map uses.

Why it exists. † Return just the positions of a dataset’s GPS-tagged images, paged, so the map view can plot them without downloading image records it will not display.

Retrieves all images in a dataset that have valid GPS metadata, with secure URLs if available — a newer endpoint for the same GPS data, used by the 3D map.

Authentication: SupabaseAuth

Uses a service-role client (row-level security bypassed).

Returns — images · totalImages · currentPage · totalPages · pageSize

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · images · thumbnails

Parameters

Name In Required Type Notes
slug path yes string
page query no integer
pageSize query no integer
sort query no string

Responses

Status Meaning Body
200 List of images with GPS data GpsImage
401 Not authenticated Error
403 Forbidden Error
404 Dataset not found Error
500 Server error Error

14.0.18 listDatasetGpsImages

GET /api/datasets/{slug}/gps-images

Lists the dataset’s images that have GPS coordinates (for map views).

Why it exists. ‡ Return a dataset’s GPS-tagged images with time-limited signed URLs, so the map can show real thumbnails at each position without exposing the underlying storage.

Called by — components/main-panel-renderer.tsx · modules/gallery/core/hooks/use-gps-images.ts

Reached from — /datasets/<slug> · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Retrieves all images in a dataset that have valid GPS metadata, with secure URLs if available.

Authentication: SupabaseAuth

Returns — success · images · totalGpsImages · truncated · limit · sasTokensGenerated

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · images

Parameters

Name In Required Type Notes
slug path yes string
limit query no integer

Responses

Status Meaning Body
200 List of images with GPS data Image
400 Bad request Error
401 Not authenticated Error
404 Dataset not found Error
500 Server error Error

14.0.19 listDatasetImageGroups

GET /api/datasets/{slug}/groups

Lists those photo pairs grouped by location for the paired viewer.

Why it exists. ‡ Group a dataset’s images into related sets by filename and folder, so a survey shot in passes can be browsed by subject rather than as one flat list.

Called by — modules/gallery/core/hooks/use-dataset-gallery-controller.ts

Reached from — /datasets/<slug> · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Retrieves paired RGB and thermal images with geodata from a dataset, organized by base filename.

Authentication: SupabaseAuth

Returns — groups · totalGroups · page · pageSize · totalPages

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · images

Parameters

Name In Required Type Notes
slug path yes string
page query no integer
categoryId query no string
folder query no string
pageSize query no integer
sort query no string

Responses

Status Meaning Body
200 Grouped images with pagination GpsImage
401 Not authenticated Error
403 Forbidden Error
404 Dataset not found Error
500 Server error Error

14.0.20 createImageUrls

POST /api/datasets/{slug}/image-url

Issues short-lived secure links to view one image — or a batch of them — in Azure storage. The dataset is authorized under the caller’s own scope before any lookup; inaccessible datasets 404 like missing ones, and cross-dataset or unknown image ids are rejected per-id.

Why it exists. ‡ Mint a short-lived signed URL for one image in a dataset, so the viewer can fetch it directly from storage without the platform proxying the bytes.

Called by — components/chat/dataset-list.tsx · components/chronological-overview.tsx · components/dataset-grid.tsx · components/main-panel-renderer.tsx …and 2 more

Reached from — /datasets/<slug> · /datasets/org/<id> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer …and 1 more

Generates read-only Azure Blob SAS URLs (60-minute expiry) for one image or a batch of images. The dataset is first resolved under the caller’s own RLS scope; a dataset the caller cannot access returns 404, indistinguishable from one that does not exist. Images are then checked per id: unknown ids and images belonging to a different dataset are rejected individually. Before issuing URLs it health-checks the storage account with a short-lived test SAS (result cached ~5 minutes); an unreachable or suspended account returns 402.

Authentication: SupabaseAuth

Returns — url · type · results · errors

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · images

Parameters

Name In Required Type Notes
slug path yes string

Request body

Field Type Required
imageId string no
imageIds array of string no

Responses

Status Meaning Body
200 SAS URL(s) generated. Single mode returns url; batch mode returns per-image results and errors maps.
400 Invalid request Error
401 Not authenticated Error
402 Storage provider suspended. Error
404 Dataset or image not found. Error
500 Server error Error

14.0.21 listDatasetImages

GET /api/datasets/{slug}/images

Lists a dataset’s images, page by page, with filters.

Why it exists. ‡ Page through a dataset’s images with the gallery’s filters — folder, category, free-text — applied in the database rather than the browser, because a campaign dataset is far too large to send whole.

Called by — app/(authenticated)/w/integrity/evidence-explorer/evidence-explorer-client.tsx · components/chronological-overview.tsx · components/workspaces/storage-client.tsx

Reached from — /w/data-explorer/storage · /w/integrity/evidence-explorer · /w/integrity/storage

Retrieves images from a dataset with pagination and filtering options.

Authentication: SupabaseAuth

Returns — filenames · totalImages · expectedTotal · rootCount · images · totalPages · currentPage

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · images

Parameters

Name In Required Type Notes
slug path yes string
pageSize query no integer
filter query no string — one of all, rgb, thermal
page query no integer
categoryId query no string
folder query no string
folders query no boolean
sort query no string
includeOtherDatasets query no boolean

Responses

Status Meaning Body
200 A paginated list of images Image
401 Not authenticated Error
403 Forbidden Error
404 Dataset not found Error
500 Server error Error

14.0.22 indexDatasetImages

POST /api/datasets/{slug}/index-images

Scans the dataset’s storage container and registers the images it finds.

Why it exists. ‡ Walk the dataset’s storage container and record every image it finds as a row, so a dataset that was registered by pointing at a bucket becomes browsable. Re-runnable as the source container grows.

Called by — app/(authenticated)/datasets/[slug]/components/reindex-button.tsx · app/datasets/create/page.tsx · components/dataset-grid.tsx · components/dataset-table.tsx …and 1 more

Reached from — /datasets/<slug> · /datasets/create · /datasets/org/<id> · /w/data-explorer/new-dataset

Indexes images in the specified dataset by scanning its storage container.

Authentication: SupabaseAuth

Returns — success · message · newImagesCount · removedCount · duplicatesRemoved · totalImagesCount · storageImagesCount

Field names as the handler returns them; the source states no types for them.

Effect — writes (delete, insert) datasets · images

Parameters

Name In Required Type Notes
slug path yes string

Request body

Field Type Required
imagesOnly boolean no

Responses

Status Meaning Body
200 Indexing result
400 Bad request Error
401 Not authenticated Error
404 Dataset not found Error
500 Server error Error

14.0.23 transitionDatasetLifecycle

PATCH /api/datasets/{slug}/lifecycle

Sends a campaign for review, or returns one for more evidence — the hand-off between Data Explorer and Integrity.

Why it exists. ‡ Let Data Explorer send a campaign for review, and let Integrity return one for more evidence — the hand-off between the two workspaces.

Called by — lib/workspaces/queries/campaign-lifecycle.ts

Reached from — /w/data-explorer/campaigns · /w/integrity/campaigns

Transitions a campaign’s lifecycle state (indexing, ready_for_review, in_review, or closed) and records the transition in an append-only event log, atomically. Authorized by the transition_dataset_lifecycle Postgres function, which admits the dataset’s owner or an organization’s admin or member (not viewers) — not the datasets UPDATE policy, since that table also carries credentials/storage_path/provider/owner_id that a blanket policy would expose. A reason is required when transitioning to indexing and rejected if blank; no reason is required for ready_for_review. Transitioning to the campaign’s current state is a no-op.

Authentication: CookieAuth, SupabaseAuth

Returns — dataset

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets

Parameters

Name In Required Type Notes
slug path yes string

Request body

Field Type Required
to string — one of indexing, ready_for_review, in_review, closed yes
reason string no

Responses

Status Meaning Body
200 The dataset row after the transition (or unchanged, if it was already at the target state). Dataset
400 Invalid to value, or a required reason was missing/blank. Error
401 Unauthorized. Error
403 The caller is neither the campaign’s owner nor an editor of its organization. Error
404 Dataset not found or not visible to the caller. Error
500 Server error. Error

14.0.24 getCampaignLifecycleEvents

GET /api/datasets/{slug}/lifecycle-events

Campaign History’s real transition timeline — who sent or returned the campaign, when, and why.

Why it exists. ‡ Give a caller the campaign’s real lifecycle transition history — who sent it for review or returned it, when, and why — the same events Campaign History renders.

Reads the campaign’s lifecycle-transition history (written only by the lifecycle-transition endpoint) newest first, with each event’s actor resolved to a name — omitted, never a raw id, when the caller cannot see the actor’s profile.

Authentication: CookieAuth, SupabaseAuth

Returns — events

Field names as the handler returns them; the source states no types for them.

Effect — reads dataset_lifecycle_events · datasets · profiles

Parameters

Name In Required Type Notes
slug path yes string

Responses

Status Meaning Body
200 The campaign’s lifecycle transitions, newest first.
400 Dataset slug is required. Error
401 Unauthorized. Error
404 Dataset not found or not visible to the caller. Error
500 Server error. Error

14.0.25 listDatasetImagePairs

GET /api/datasets/{slug}/pairs

Lists matched pairs of regular and thermal photos, page by page.

Why it exists. ‡ Match each thermal image in a dataset to its visual counterpart by filename convention, so the side-by-side comparison view can show both captures of the same subject.

Called by — app/(authenticated)/w/integrity/investigate/investigate-client.tsx

Reached from — /w/integrity/investigate

Retrieves paginated pairs of RGB and thermal images from a dataset.

Authentication: SupabaseAuth

Returns — pairs · totalItems · totalPages · currentPage

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · images · organization_members

Parameters

Name In Required Type Notes
slug path yes string
page query no integer
forImageId query no string

Responses

Status Meaning Body
200 A paginated list of image pairs ImagePair
401 Not authenticated Error
403 Forbidden Error
404 Dataset not found Error
500 Server error Error

14.0.26 getCampaignProgress

GET /api/datasets/{slug}/progress

The same per-campaign numbers the /progress dashboard shows — coverage, tagged/anomaly counts, and new/confirmed/dismissed candidates.

Why it exists. ‡ Give a caller the same per-campaign progress numbers the /progress dashboard shows, without needing the dashboard’s own server-only queries.

Composes the same coverage, tagged/anomaly-count, and new/confirmed/dismissed-candidate numbers the /progress dashboard shows, running as the caller under RLS rather than the page’s service-role client.

Authentication: CookieAuth, SupabaseAuth

Returns — datasetSlug · datasetName · totalImages · gpsImages · coveragePct · taggedImages · anomalyRatePct · candidates

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · equipment_damage_mechanisms · images

Parameters

Name In Required Type Notes
slug path yes string

Responses

Status Meaning Body
200 Coverage/tagged/anomaly numbers plus candidate counts for this campaign.
400 Dataset slug is required. Error
401 Unauthorized. Error
404 Dataset not found or not visible to the caller. Error
500 Server error. Error

14.0.27 createDatasetVideoUrl

POST /api/datasets/{slug}/sign-video

Issues a short-lived secure link to play a video from the dataset.

Why it exists. ‡ Mint a short-lived signed URL for one video in a dataset, so it can be streamed straight from storage by a caller who is a member of the owning organisation.

Called by — modules/gallery/overlays/components/image-viewer.tsx · modules/photo-map-3d/core/hooks/use-click-handlers.ts · modules/photo-map-3d/overlays/hooks/use-photo-viewer.ts

Reached from — /datasets/<slug> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Mints a short-lived read-only Azure Blob SAS URL for a video blob in the dataset’s container. Requires membership of the dataset’s organization; the dataset’s storage_path/credentials supply the Azure account and key.

Authentication: CookieAuth

Returns — url

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets

Parameters

Name In Required Type Notes
slug path yes string

Request body

Field Type Required
blobName string yes
expiryMinutes number no

Responses

Status Meaning Body
200 Signed URL.
400 Missing slug or blobName. Error
401 Unauthorized. Error
403 Forbidden — not a member of the dataset’s organization. Error
404 Dataset not found. Error
500 Invalid storage configuration or signing failure. Error