> For the complete documentation index, see [llms.txt](https://movemint.gitbook.io/movemint-developer-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://movemint.gitbook.io/movemint-developer-docs/open-api-specification/core-resources/marketing-photos.md).

# Marketing Photos

Manage the marketing photo album that backs an event's public page. Photos are ingested from a URL, processed asynchronously, and can be reordered or promoted to the event's cover photo.

## Copy marketing photos from another event

> Copies marketing photos onto this event from another event the caller\
> administers — reusing a previous edition's gallery, or a sibling race's,\
> without downloading and re-uploading the images.\
> \
> The copies are independent photo records: deleting one from either\
> event leaves the other's gallery alone. They are appended after\
> whatever this event already has, in the order they sit in the source\
> album (not the order \`photo\_uuids\` names them).\
> \
> \*\*Authorized on both ends.\*\* The caller must administer this event\
> \*and\* the source event, each with the \`marketing\` scope. A source event\
> the caller does not administer returns \`403\` and copies nothing.\
> \
> Omitting \`photo\_uuids\` copies the whole source gallery. A uuid that is\
> not on the source event returns \`422\` naming it, rather than being\
> silently skipped. A photo whose stored image cannot be read is skipped\
> and counted in \`failed\`.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Marketing Photos","description":"Manage the marketing photo album that backs an event's public page.\nPhotos are ingested from a URL, processed asynchronously, and can be\nreordered or promoted to the event's cover photo.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}}},"schemas":{"CopyMarketingPhotosRequest":{"type":"object","required":["source_event_handle"],"properties":{"source_event_handle":{"type":"string","description":"Handle of the event to copy photos from. Must be an event the\ncaller administers with the `marketing` scope, and must not be this\nevent.\n"},"photo_uuids":{"type":"array","items":{"type":"string"},"description":"Uuids of the specific photos to copy. Omit to copy the whole source\ngallery.\n"}}},"CopyMarketingPhotosResponse":{"type":"object","required":["copied","failed","source_event_handle","photos"],"properties":{"copied":{"type":"integer","description":"How many photos were copied."},"failed":{"type":"integer","description":"How many were skipped because their stored image could not be read.\n"},"source_event_handle":{"type":"string","description":"Handle of the event the photos came from."},"photos":{"type":"array","description":"This event's gallery after the copy, in display order.","items":{"$ref":"#/components/schemas/MarketingPhoto"}}}},"MarketingPhoto":{"type":"object","required":["uuid","status","sizes"],"properties":{"uuid":{"type":"string","description":"Stable identifier for the photo across endpoints."},"status":{"type":"string","description":"Lifecycle state. `ready` means processing is complete and the\n`sizes` map is populated; other values indicate the ingest\npipeline is still running.\n"},"sizes":{"type":"object","description":"Map of size name (e.g., `original`, `lg`, `md`, `sm`) to\n`{ url, width, height }`. May be empty/partial while the photo\nis still processing.\n","additionalProperties":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"width":{"type":"integer"},"height":{"type":"integer"}}}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}},"ValidationError":{"type":"object","description":"A validation error response with a list of error messages","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"},"description":"List of validation error messages"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EventNotFound":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/marketing_photo_copies":{"post":{"tags":["Marketing Photos"],"summary":"Copy marketing photos from another event","description":"Copies marketing photos onto this event from another event the caller\nadministers — reusing a previous edition's gallery, or a sibling race's,\nwithout downloading and re-uploading the images.\n\nThe copies are independent photo records: deleting one from either\nevent leaves the other's gallery alone. They are appended after\nwhatever this event already has, in the order they sit in the source\nalbum (not the order `photo_uuids` names them).\n\n**Authorized on both ends.** The caller must administer this event\n*and* the source event, each with the `marketing` scope. A source event\nthe caller does not administer returns `403` and copies nothing.\n\nOmitting `photo_uuids` copies the whole source gallery. A uuid that is\nnot on the source event returns `422` naming it, rather than being\nsilently skipped. A photo whose stored image cannot be read is skipped\nand counted in `failed`.\n","operationId":"copyEventMarketingPhotos","parameters":[{"$ref":"#/components/parameters/EventHandle"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopyMarketingPhotosRequest"}}}},"responses":{"201":{"description":"What was copied, plus this event's resulting gallery","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopyMarketingPhotosResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EventNotFound"},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}}}}
````

## List marketing photos for an event

> Returns the event's marketing album in display order. Photos that are\
> still ingesting will have \`status\` set to a non-\`ready\` value and an\
> empty/partial \`sizes\` map.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Marketing Photos","description":"Manage the marketing photo album that backs an event's public page.\nPhotos are ingested from a URL, processed asynchronously, and can be\nreordered or promoted to the event's cover photo.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}}},"schemas":{"MarketingPhoto":{"type":"object","required":["uuid","status","sizes"],"properties":{"uuid":{"type":"string","description":"Stable identifier for the photo across endpoints."},"status":{"type":"string","description":"Lifecycle state. `ready` means processing is complete and the\n`sizes` map is populated; other values indicate the ingest\npipeline is still running.\n"},"sizes":{"type":"object","description":"Map of size name (e.g., `original`, `lg`, `md`, `sm`) to\n`{ url, width, height }`. May be empty/partial while the photo\nis still processing.\n","additionalProperties":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"width":{"type":"integer"},"height":{"type":"integer"}}}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EventNotFound":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/marketing_photos":{"get":{"tags":["Marketing Photos"],"summary":"List marketing photos for an event","description":"Returns the event's marketing album in display order. Photos that are\nstill ingesting will have `status` set to a non-`ready` value and an\nempty/partial `sizes` map.\n","operationId":"listMarketingPhotos","parameters":[{"$ref":"#/components/parameters/EventHandle"}],"responses":{"200":{"description":"Ordered list of photos","content":{"application/json":{"schema":{"type":"object","required":["photos"],"properties":{"photos":{"type":"array","items":{"$ref":"#/components/schemas/MarketingPhoto"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EventNotFound"}}}}}}
````

## Add a marketing photo from a URL

> Ingests an image from a publicly accessible URL into the event's\
> marketing album. The URL is fetched server-side via the Strive safe\
> fetcher; private network ranges and oversized payloads are\
> rejected. Processing (thumbnails, sizes) happens asynchronously, so\
> the returned photo may still be processing.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Marketing Photos","description":"Manage the marketing photo album that backs an event's public page.\nPhotos are ingested from a URL, processed asynchronously, and can be\nreordered or promoted to the event's cover photo.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}}},"schemas":{"CreateMarketingPhotoRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Publicly accessible image URL."}}},"MarketingPhoto":{"type":"object","required":["uuid","status","sizes"],"properties":{"uuid":{"type":"string","description":"Stable identifier for the photo across endpoints."},"status":{"type":"string","description":"Lifecycle state. `ready` means processing is complete and the\n`sizes` map is populated; other values indicate the ingest\npipeline is still running.\n"},"sizes":{"type":"object","description":"Map of size name (e.g., `original`, `lg`, `md`, `sm`) to\n`{ url, width, height }`. May be empty/partial while the photo\nis still processing.\n","additionalProperties":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"width":{"type":"integer"},"height":{"type":"integer"}}}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EventNotFound":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/marketing_photos":{"post":{"tags":["Marketing Photos"],"summary":"Add a marketing photo from a URL","description":"Ingests an image from a publicly accessible URL into the event's\nmarketing album. The URL is fetched server-side via the Strive safe\nfetcher; private network ranges and oversized payloads are\nrejected. Processing (thumbnails, sizes) happens asynchronously, so\nthe returned photo may still be processing.\n","operationId":"createMarketingPhoto","parameters":[{"$ref":"#/components/parameters/EventHandle"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMarketingPhotoRequest"}}}},"responses":{"201":{"description":"Photo enqueued for ingestion","content":{"application/json":{"schema":{"type":"object","required":["photo"],"properties":{"photo":{"$ref":"#/components/schemas/MarketingPhoto"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EventNotFound"},"422":{"description":"Bad URL or ingestion error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## Reorder photos in the marketing album

> Replaces the album's order with the order of the supplied UUIDs. Any\
> UUIDs not present in the album are rejected with \`422\`; UUIDs in the\
> album that are omitted from the request are appended after the\
> supplied ones in their existing relative order.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Marketing Photos","description":"Manage the marketing photo album that backs an event's public page.\nPhotos are ingested from a URL, processed asynchronously, and can be\nreordered or promoted to the event's cover photo.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}}},"schemas":{"ReorderMarketingPhotosRequest":{"type":"object","required":["uuids"],"properties":{"uuids":{"type":"array","description":"Photo UUIDs in the desired display order.","items":{"type":"string"}}}},"MarketingPhoto":{"type":"object","required":["uuid","status","sizes"],"properties":{"uuid":{"type":"string","description":"Stable identifier for the photo across endpoints."},"status":{"type":"string","description":"Lifecycle state. `ready` means processing is complete and the\n`sizes` map is populated; other values indicate the ingest\npipeline is still running.\n"},"sizes":{"type":"object","description":"Map of size name (e.g., `original`, `lg`, `md`, `sm`) to\n`{ url, width, height }`. May be empty/partial while the photo\nis still processing.\n","additionalProperties":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"width":{"type":"integer"},"height":{"type":"integer"}}}}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"EventNotFound":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/events/{handle}/marketing_photos/reorder":{"post":{"tags":["Marketing Photos"],"summary":"Reorder photos in the marketing album","description":"Replaces the album's order with the order of the supplied UUIDs. Any\nUUIDs not present in the album are rejected with `422`; UUIDs in the\nalbum that are omitted from the request are appended after the\nsupplied ones in their existing relative order.\n","operationId":"reorderMarketingPhotos","parameters":[{"$ref":"#/components/parameters/EventHandle"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderMarketingPhotosRequest"}}}},"responses":{"200":{"description":"New order","content":{"application/json":{"schema":{"type":"object","required":["photos"],"properties":{"photos":{"type":"array","items":{"$ref":"#/components/schemas/MarketingPhoto"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/EventNotFound"},"422":{"description":"UUIDs missing or unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## Remove a marketing photo from the album

> Removes the photo from the marketing album and deletes the underlying\
> photo record. Note that if the photo was previously promoted to the\
> event cover photo, the cover photo's bytes were copied — deleting\
> the marketing photo does not remove the cover photo.<br>

````json
{"openapi":"3.1.0","info":{"title":"Movemint API","version":"1.0"},"tags":[{"name":"Marketing Photos","description":"Manage the marketing photo album that backs an event's public page.\nPhotos are ingested from a URL, processed asynchronously, and can be\nreordered or promoted to the event's cover photo.\n"}],"servers":[{"url":"https://www.movemint.cc","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Pass the access token in the `Authorization` header:\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n"},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authentication using the Authorization Code or Client\nCredentials grant flow.\n","flows":{"authorizationCode":{"authorizationUrl":"https://www.movemint.cc/oauth/authorize","tokenUrl":"https://www.movemint.cc/oauth/token","refreshUrl":"https://www.movemint.cc/oauth/token","scopes":{}},"clientCredentials":{"tokenUrl":"https://www.movemint.cc/oauth/token","scopes":{}}}}},"parameters":{"EventHandle":{"name":"handle","in":"path","required":true,"description":"The unique handle (slug) of the event","schema":{"type":"string"}}},"responses":{"Unauthorized":{"description":"Unauthorized - invalid or missing token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"Forbidden":{"description":"Forbidden - user is not an admin of this event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"},"error_description":{"type":"string","description":"Human-readable description of the error"}}},"Error":{"type":"object","description":"A generic error response","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message"}}}}},"paths":{"/api/v1/events/{handle}/marketing_photos/{uuid}":{"delete":{"tags":["Marketing Photos"],"summary":"Remove a marketing photo from the album","description":"Removes the photo from the marketing album and deletes the underlying\nphoto record. Note that if the photo was previously promoted to the\nevent cover photo, the cover photo's bytes were copied — deleting\nthe marketing photo does not remove the cover photo.\n","operationId":"deleteMarketingPhoto","parameters":[{"$ref":"#/components/parameters/EventHandle"},{"name":"uuid","in":"path","required":true,"description":"The UUID of the photo to delete.","schema":{"type":"string"}}],"responses":{"200":{"description":"Photo deleted","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean","enum":[true]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Event or photo not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````
