Pavoot Application API

The authenticated backend for the Pavoot AI event platform. A FastAPI service publishing a complete OpenAPI 3.1.0 document at https://api.pavoot.com/openapi.json covering 248 operations and 56 request schemas: event (project) management, S3 presigned single-PUT and multipart media upload, a three-step asynchronous AI tagging pipeline (general, AWS Rekognition, BDA), face detection and person identity resolution with machine-proposed merge suggestions, sponsor brand and logo training and recognition, attendee registration with mandatory face capture, attendee follow-up email workflow, shareable galleries with review and flagging, photographer and recipient access management, storage and upload quotas, and a fine-grained organization permission system. Authenticated with Clerk-issued session JWTs; the published spec declares no securitySchemes, no servers, no tags and no typed responses, so the auth model, host and error surface in this profile were established by probing the live API. Not a developer product — there is no API key issuance, no plan, no rate limit, no versioning and no changelog.

Documentation

Specifications

Other Resources

OpenAPI Specification

pavoot-api-openapi.json Raw ↑
{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/emit_counts":{"post":{"summary":"Emit Counts","operationId":"emit_counts_emit_counts_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CountsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/emit_person_updated":{"post":{"summary":"Emit Person Updated Endpoint","operationId":"emit_person_updated_endpoint_emit_person_updated_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonUpdatedRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/emit_tagging_step":{"post":{"summary":"Emit Tagging Step","description":"Internal endpoint called by Lambda workers after each tagging step completes.","operationId":"emit_tagging_step_emit_tagging_step_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaggingStepRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/addImages":{"post":{"summary":"Add Images Route","description":"Endpoint for registering multiple images by UUID and queuing tagging tasks to SQS.\n\nSync on purpose: every call inside add_images blocks (SQLAlchemy, boto3), so as\nan async handler a 50-image upload held the event loop for seconds and stalled\nevery other request on the worker. FastAPI runs sync handlers in its threadpool.","operationId":"add_images_route_addImages_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddImagesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/images/tagging-failures":{"get":{"summary":"Get Tagging Failures","description":"Returns images that have failed or stuck tagging steps for a project.\nFailed = status 'failed'. Stuck = status 'pending' for more than 1 hour.\nFilter by steps using ?steps=general,rekognition,bda (defaults to all three).\nUse operator=AND to require ALL selected steps to match, OR to match any.\nUse statuses=failed,stuck,pending to control which statuses are included.","operationId":"get_tagging_failures_projects__project_id__images_tagging_failures_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"integer","title":"Project Id"}},{"name":"steps","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Steps"}},{"name":"operator","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"OR","title":"Operator"}},{"name":"statuses","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Statuses"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/projects/{project_id}/images/retry-tagging":{"post":{"summary":"Retry Tagging","description":"Retry specific tagging steps for specific images.\nResets status to 'pending' and re-queues to the appropriate SQS queues.","operationId":"retry_tagging_projects__project_id__images_retry_tagging_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"integer","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryTaggingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deleteImages":{"post":{"summary":"Delete Images Endpoint","description":"Deletes multiple images from the system.\n- If images are NOT in trash: performs soft delete (moves to trash)\n- If images are already in trash: performs permanent delete (removes from S3 and DB)\n\nExpects:\n    - JSON with:\n        - 'imageIds': list of image IDs to delete\n        - 'projectId'\n\nReturns:\n    - Success message with counts of deleted images\n    - Error if imageIds is missing or invalid","operationId":"delete_images_endpoint_deleteImages_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteImagesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/getTrashImages":{"get":{"summary":"Get Trash Images Endpoint","description":"Endpoint for retrieving deleted images (trash) for a single project, or across\nmultiple projects (the \"All Projects\" view) when projectIds[] is provided.","operationId":"get_trash_images_endpoint_getTrashImages_get","parameters":[{"name":"projectId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Projectid"}},{"name":"projectIds[]","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Projectids[]"}},{"name":"photographerId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photographerid"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":1,"title":"Page"}},{"name":"pageSize","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":40,"title":"Pagesize"}},{"name":"sortBy","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sortby"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/restoreImages":{"post":{"summary":"Restore Images Endpoint","description":"Restores multiple images from trash (undeletes them).\n\nExpects:\n    - JSON with:\n        - 'imageIds': list of image IDs to restore\n        - 'projectId'\n\nReturns:\n    - Success message with counts of restored images\n    - Error if imageIds is missing or invalid","operationId":"restore_images_endpoint_restoreImages_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreImagesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/countTrashImages":{"get":{"summary":"Count Trash Images Endpoint","description":"Count deleted images (trash) for a project or multiple projects. For photographer role, returns only that photographer's count (backend enforces).","operationId":"count_trash_images_endpoint_countTrashImages_get","parameters":[{"name":"projectId","in":"query","required":false,"schema":{"type":"integer","title":"Projectid"}},{"name":"projectIds[]","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Projectids[]"}},{"name":"photographerId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photographerid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/countImages":{"get":{"summary":"Count Images Endpoint","description":"Count images for a project or multiple projects. For photographer role, returns only that photographer's count (backend enforces).","operationId":"count_images_endpoint_countImages_get","parameters":[{"name":"projectId","in":"query","required":false,"schema":{"type":"integer","title":"Projectid"}},{"name":"projectIds[]","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Projectids[]"}},{"name":"photographerId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photographerid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/getImages":{"get":{"summary":"Get Images Endpoint","description":"Endpoint for retrieving images with optional filters.\nNote: aiQuery should be sent via POST /searchImages endpoint.","operationId":"get_images_endpoint_getImages_get","parameters":[{"name":"id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"}},{"name":"startDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Startdate"}},{"name":"endDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Enddate"}},{"name":"imageTitle","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Imagetitle"}},{"name":"projectId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Projectid"}},{"name":"projectIds[]","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[],"title":"Projectids[]"}},{"name":"photographerId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photographerid"}},{"name":"folderName","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Foldername"}},{"name":"personCategory","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Personcategory"}},{"name":"tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[],"title":"Tags"}},{"name":"tagsByCategory","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tagsbycategory"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":1,"title":"Page"}},{"name":"pageSize","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":10,"title":"Pagesize"}},{"name":"dateTakenStart","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Datetakenstart"}},{"name":"dateTakenEnd","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Datetakenend"}},{"name":"dateUploadedStart","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dateuploadedstart"}},{"name":"dateUploadedEnd","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dateuploadedend"}},{"name":"filterTagsWithOr","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Filtertagswithor"}},{"name":"sortBy","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sortby"}},{"name":"dateTakenNull","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datetakennull"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/searchImages":{"post":{"summary":"Search Images Endpoint","description":"Endpoint for searching images with AI boolean query AST.\nAccepts aiQuery in request body along with other filters.","operationId":"search_images_endpoint_searchImages_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/generateThumbnail/{image_id}":{"post":{"summary":"Generate Thumbnail Endpoint","description":"Generates a thumbnail for the given image if it doesn't exist, uploads it to S3,\nand marks has_thumbnail = TRUE in the DB. Returns the presigned thumbnail URL.","operationId":"generate_thumbnail_endpoint_generateThumbnail__image_id__post","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","title":"Image Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/regenerateThumbnails":{"post":{"summary":"Regenerate Thumbnails Endpoint","description":"Maintenance: regenerate (overwrite) correctly-oriented thumbnails at the canonical key\n`thumbnails/{project_id}/{image_id}`. Fixes STALE/rotated thumbnail objects that were\nwritten by an older pipeline before EXIF orientation was baked in. Reuses the same\nload_downscaled_rgb path as /generateThumbnail (orientation baked into pixels).\n\nBody: {\"projectId\": <id>} to regenerate every non-deleted image in a project, or\n{\"imageIds\": [...]} for a specific set. Runs in the background; returns 202-style\n{\"success\": true, \"queued\": N}. Idempotent — safe to re-run.","operationId":"regenerate_thumbnails_endpoint_regenerateThumbnails_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/getImagesWithUnknownFaces":{"get":{"summary":"Get Images With Unknown Faces Endpoint","description":"Retrieves images that have at least one face with an unknown name (name IS NULL OR name = '?').\nThis is optimized to only return images that actually have unknown faces.","operationId":"get_images_with_unknown_faces_endpoint_getImagesWithUnknownFaces_get","parameters":[{"name":"projectId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Projectid"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":1,"title":"Page"}},{"name":"pageSize","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":50,"title":"Pagesize"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/generateDownloadUrl":{"post":{"summary":"Generate Download Url Endpoint","description":"Generates a presigned URL for downloading images. If only one image is requested,\nreturns a direct download URL for that image. If multiple images, creates a ZIP file.\n\nRequest Body:\n    imageIds: List of image IDs to include\n    projectId: Project ID for the images\n    \nReturns:\n    JSON response with:\n        - downloadUrl: The presigned URL\n        - filename: The filename for download\n        - isZip: Boolean indicating if it's a ZIP file or single image\n    \nErrors:\n    - 500 if download generation fails","operationId":"generate_download_url_endpoint_generateDownloadUrl_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZipDownloadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/generatePresignedUrls":{"post":{"summary":"Generate Presigned Urls Endpoint","description":"Generates presigned URLs for uploading files to S3.\n\nRequest Body:\n    files (list): List of file information with contentType (required)\n    projectId (int): The ID of the project (required)\n    folderName (str): Optional folder name for organization\n    photographerId (str): Optional ID of the photographer\n    \nReturns:\n    JSON response with:\n        - urls: List of objects containing file id, key, presigned URL, and contentType\n        \nErrors:\n    - 401 if user is not authenticated\n    - 403 if user lacks permission\n    - 400 if required parameters are missing\n    - 500 if S3 service access fails","operationId":"generate_presigned_urls_endpoint_generatePresignedUrls_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeneratePresignedUrlsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/createMultipartUpload":{"post":{"summary":"Create Multipart Upload Endpoint","description":"Initiate S3 multipart uploads and return presigned part URLs. Used for\nlarge files so the browser can upload parts in parallel (small files still\nuse the single-PUT /generatePresignedUrls path).","operationId":"create_multipart_upload_endpoint_createMultipartUpload_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMultipartUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/completeMultipartUpload":{"post":{"summary":"Complete Multipart Upload Endpoint","operationId":"complete_multipart_upload_endpoint_completeMultipartUpload_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteMultipartUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/abortMultipartUpload":{"post":{"summary":"Abort Multipart Upload Endpoint","operationId":"abort_multipart_upload_endpoint_abortMultipartUpload_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbortMultipartUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/createFolder":{"post":{"summary":"Create Folder Endpoint","operationId":"create_folder_endpoint_createFolder_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deleteFolder":{"post":{"summary":"Delete Folder Endpoint","operationId":"delete_folder_endpoint_deleteFolder_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deleteFolderWithContents":{"post":{"summary":"Delete Folder With Contents Endpoint","operationId":"delete_folder_with_contents_endpoint_deleteFolderWithContents_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/updateFolderName":{"put":{"summary":"Update Folder Name Endpoint","operationId":"update_folder_name_endpoint_updateFolderName_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFolderNameRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/getProjectFolders":{"get":{"summary":"Get Project Folders Endpoint","description":"Gets all folders for a specified project or the union across multiple projects.","operationId":"get_project_folders_endpoint_getProjectFolders_get","parameters":[{"name":"projectId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Projectid"}},{"name":"projectIds[]","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Projectids[]"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"photographerId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Photographerid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/addImagesToFolder":{"post":{"summary":"Add Image To Folder Endpoint","description":"Associates multiple images with a folder by adding the folder name as a tag.\n\nExpects:\n    - JSON with:\n        - 'imageIds': list of image IDs to add to the folder\n        - 'folderName': the name of the folder to add the images to\n        - 'projectId'\n\nProcess:\n    1. Gets the project ID for each image\n    2. Checks if the folder exists for each project\n    3. Adds the folder name as a tag for each image that doesn't already have it\n\nReturns:\n    - Success message with results for each image\n    - Detailed results including successful and failed operations","operationId":"add_image_to_folder_endpoint_addImagesToFolder_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModifyFolderImagesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/removeImagesFromFolder":{"post":{"summary":"Remove Image From Folder Endpoint","description":"Removes multiple images from a folder by deleting the folder name tag.\n\nExpects:\n    - JSON with:\n        - 'imageIds': list of image IDs to remove from the folder\n        - 'folderName': the name of the folder to remove the images from\n        - 'projectId'\n\nProcess:\n    1. Gets the project ID for each image\n    2. Checks if the folder exists for each project\n    3. Removes the folder name tag for each image if it exists\n\nReturns:\n    - Success message with results for each image\n    - Detailed results including successful and failed operations","operationId":"remove_image_from_folder_endpoint_removeImagesFromFolder_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModifyFolderImagesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/updateImagesPhotographer":{"post":{"summary":"Update Images Photographer Endpoint","description":"Sets the photographer for the given images in the project.\nExpects: projectId, imageIds, photographerId.\nPhotographer must be associated with the project.","operationId":"update_images_photographer_endpoint_updateImagesPhotographer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateImagesPhotographerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/getPhotographers":{"get":{"summary":"Get Photographers Endpoint","description":"Retrieves photographers for a single project, or the union across multiple\nprojects (the \"All Projects\" view).\n\nQuery Parameters (one of):\n    - projectId: A single project ID\n    - projectIds[]: Multiple project IDs. Photographers are merged by identity\n      (id) across projects, with totalImagesCount summed across them.\n\nReturns:\n    JSON response with list of photographers containing:\n    - id: unique identifier\n    - name: photographer's name\n    - email: photographer's email address\n    - description: additional information about the photographer\n    - isEventOrganizer: boolean indicating if photographer is also an event organizer\n    - totalImagesCount: total number of images uploaded by this photographer\n\nErrors:\n    - 400 if neither projectId nor projectIds[] is provided\n    - 500 if database access fails","operationId":"get_photographers_endpoint_getPhotographers_get","parameters":[{"name":"projectId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Projectid"}},{"name":"projectIds[]","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Projectids[]"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/getRecipients":{"get":{"summary":"Get Recipients Endpoint","description":"Retrieves recipients for a single project, or the union across multiple\nprojects (the \"All Projects\" view) when projectIds[] is provided. The\nmulti-project payload includes per-recipient project membership (projectIds).","operationId":"get_recipients_endpoint_getRecipients_get","parameters":[{"name":"projectId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Projectid"}},{"name":"projectIds[]","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Projectids[]"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/addRecipientToProjects":{"post":{"summary":"Add Recipient To Projects Endpoint","description":"Attach an EXISTING recipient to additional projects (link-only). Used by the\nAll-Projects recipients panel. Body: { recipientId: str, projectIds: int[] }.","operationId":"add_recipient_to_projects_endpoint_addRecipientToProjects_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deletePhotographer":{"post":{"summary":"Delete Photographer Endpoint","description":"Removes a photographer's association with a specific project.\n\nExpects:\n    - JSON with:\n        - 'photographerId': unique identifier of the photographer\n        - 'projectId': ID of the project to remove from\n\nReturns:\n    - Success message if photographer was found and removed from project\n    - Error if photographer not found or other issues occur","operationId":"delete_photographer_endpoint_deletePhotographer_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/revokePhotographerProjectAccess":{"post":{"summary":"Revoke Photographer Project Access Endpoint","description":"Takes away a photographer's access to ONE project, keeping their row and image credit.\n\nExpects:\n    - JSON with 'photographerId' and 'projectId'\n\nCounterpart to /invitePhotographer, which is the only thing that grants a project.\n/deletePhotographer is NOT the counterpart: it drops the association row, which also\ndrops the photographer out of that project's list. Idempotent — revoking access nobody\nhas is a success with changed=false.","operationId":"revoke_photographer_project_access_endpoint_revokePhotographerProjectAccess_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deleteRecipient":{"post":{"summary":"Delete Recipient Endpoint","description":"Removes a recipient's association with a specific project.\n\nExpects:\n    - JSON with:\n        - 'recipientId': unique identifier of the recipient\n        - 'projectId': ID of the project to remove from\n\nReturns:\n    - Success message if recipient was found and removed from project\n    - Error if recipient not found or other issues occur","operationId":"delete_recipient_endpoint_deleteRecipient_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/editPhotographer":{"post":{"summary":"Edit Photographer Endpoint","description":"Updates a photographer's information in the database.\n\nExpects:\n    - JSON with:\n        - 'id': unique identifier of the photographer (required)\n        - 'name': new name for the photographer (optional)\n        - 'description': new description for the photographer (optional)\n        - 'projectId'\n\nReturns:\n    - Success message if photographer was found and updated\n    - Error if photographer not found or other issues occur","operationId":"edit_photographer_endpoint_editPhotographer_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/editRecipient":{"post":{"summary":"Edit Recipient Endpoint","description":"Updates a recipient's information in the database.\n\nExpects:\n    - JSON with:\n        - 'recipientId': unique identifier of the recipient (required)\n        - 'name': new name for the recipient (optional)\n        - 'primaryEmail': new primary email for the recipient (optional)\n        - 'description': new description for the recipient (optional)\n        - 'additionalEmails': list of additional emails (optional)\n        - 'projectId'\n\nReturns:\n    - Success message if rec

# --- truncated at 32 KB (211 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/pavoot/refs/heads/main/openapi/pavoot-api-openapi.json