Hydra is now in beta|Get started free|Follow our journey on X.com

Customer Photos API
On this page

Customer Photos

The customer photos API enables merchants to collect and moderate user-generated content (UGC) photos from customers. Photos can be submitted via the storefront (publishable key) or uploaded directly by admins (secret key). All submissions start in a pending state and require moderation before appearing on the storefront.

Use cases:

  • Product page photo galleries showing real customer photos
  • Social proof and trust signals on product pages
  • User-generated content for marketing campaigns
  • Customer submissions from Instagram, reviews, or email

Base URL: https://api.hydrajs.dev

Endpoints

Method Path Auth Description
GET /v1/customer-photos Secret List customer photos
POST /v1/customer-photos Publishable Upload customer photo
GET /v1/customer-photos/{id} Secret Get customer photo
PATCH /v1/customer-photos/{id} Secret Update customer photo
POST /v1/customer-photos/{id}/approve Secret Approve customer photo
POST /v1/customer-photos/{id}/reject Secret Reject customer photo
POST /v1/customer-photos/{id}/replace-image Secret Replace customer photo image
DELETE /v1/customer-photos/{id} Secret Delete customer photo
GET /v1/products/{id}/customer-photos Publishable Get approved product photos

List customer photos

GET /v1/customer-photos

Returns a cursor-paginated list of customer photos. Supports filtering by product and moderation status. Requires a secret key.

Query parameters

Parameter Type Description
limit integer Results per page (1–100, default 25)
cursor string Pagination cursor from a previous response
product_id string Filter by product ID
status string Filter by status: pending, approved, rejected
sort string Sort field: created_at (default), updated_at
order string Sort direction: asc, desc (default)

Request

curl "https://api.hydrajs.dev/v1/customer-photos?status=pending&limit=10" \
  -H "Authorization: Bearer sk_YOUR_KEY"

Response 200

{
  "data": [
    {
      "id": "cp_abc123",
      "product_id": "prod_xyz789",
      "image_url": "https://cdn.hydrajs.dev/store_abc/ugc/a1b2c3d4.jpg",
      "bucket_path": "store_abc/ugc/a1b2c3d4.jpg",
      "width": 1200,
      "height": 1500,
      "aspect_ratio": "4:5",
      "file_size": 245678,
      "credit": "@janedoe",
      "link_url": "https://instagram.com/janedoe",
      "link_text": "View on Instagram",
      "customer_id": null,
      "customer_email": "jane@example.com",
      "alt_text": "Customer wearing navy organic cotton t-shirt",
      "show_in_gallery": true,
      "status": "pending",
      "moderated_at": null,
      "moderated_by": null,
      "moderation_notes": null,
      "created_at": "2026-09-15T14:30:00.000Z",
      "updated_at": "2026-09-15T14:30:00.000Z"
    }
  ],
  "pagination": {
    "cursor": "eyJ0IjoiMjAyNi...",
    "has_more": false,
    "total": 1
  }
}

Upload customer photo

POST /v1/customer-photos

Upload a customer photo for a product. Accepts multipart/form-data with an image file and metadata. Publishable or secret keys work — publishable-key uploads start in pending status, while admin uploads (secret key) are auto-approved.

Request body (multipart/form-data)

Field Type Required Description
image file Yes Image file (JPEG, PNG, WebP, GIF, AVIF, HEIC, max 10MB)
product_id string Yes Product ID to attach photo to
credit string No Photo credit (name or @handle, max 100 chars)
link_url string No External link URL (e.g. Instagram profile)
link_text string No Link text display (max 50 chars)
alt_text string No Image alt text for accessibility (max 500 chars)
customer_email string No Customer email (for guest submissions)

Request

curl -X POST "https://api.hydrajs.dev/v1/customer-photos" \
  -H "Authorization: Bearer pk_YOUR_KEY" \
  -F "image=@customer-photo.jpg" \
  -F "product_id=prod_xyz789" \
  -F "credit=@janedoe" \
  -F "link_url=https://instagram.com/janedoe" \
  -F "customer_email=jane@example.com"

Response 201

{
  "data": {
    "id": "cp_abc123",
    "product_id": "prod_xyz789",
    "image_url": "https://cdn.hydrajs.dev/store_abc/ugc/a1b2c3d4.jpg",
    "status": "pending",
    "credit": "@janedoe",
    "created_at": "2026-09-15T14:30:00.000Z"
  }
}

Get customer photo

GET /v1/customer-photos/{id}

Get a single customer photo by ID. Returns full photo details including moderation status and metadata.

Path parameters

Parameter Type Description
id string Customer photo ID

Request

curl "https://api.hydrajs.dev/v1/customer-photos/cp_abc123" \
  -H "Authorization: Bearer sk_YOUR_KEY"

Response 200

{
  "data": {
    "id": "cp_abc123",
    "product_id": "prod_xyz789",
    "image_url": "https://cdn.hydrajs.dev/store_abc/ugc/a1b2c3d4.jpg",
    "bucket_path": "store_abc/ugc/a1b2c3d4.jpg",
    "width": 1200,
    "height": 1500,
    "aspect_ratio": "4:5",
    "file_size": 245678,
    "credit": "@janedoe",
    "link_url": "https://instagram.com/janedoe",
    "link_text": "View on Instagram",
    "customer_id": null,
    "customer_email": "jane@example.com",
    "alt_text": "Customer wearing navy organic cotton t-shirt",
    "show_in_gallery": true,
    "status": "approved",
    "moderated_at": "2026-09-15T15:00:00.000Z",
    "moderated_by": "admin@store.com",
    "moderation_notes": null,
    "created_at": "2026-09-15T14:30:00.000Z",
    "updated_at": "2026-09-15T15:00:00.000Z"
  }
}

Update customer photo

PATCH /v1/customer-photos/{id}

Update customer photo metadata. Only include the fields you want to change. Status changes should use the dedicated approve/reject endpoints.

Path parameters

Parameter Type Description
id string Customer photo ID

Request body

Field Type Description
credit string Photo credit (name or @handle, max 100 chars)
link_url string External link URL
link_text string Link text display (max 50 chars)
alt_text string Image alt text for accessibility (max 200 chars)
show_in_gallery boolean Whether to show in product gallery

Request

curl -X PATCH "https://api.hydrajs.dev/v1/customer-photos/cp_abc123" \
  -H "Authorization: Bearer sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "alt_text": "Customer wearing navy organic cotton t-shirt outdoors",
    "show_in_gallery": true
  }'

Response 200

{
  "data": {
    "id": "cp_abc123",
    "product_id": "prod_xyz789",
    "alt_text": "Customer wearing navy organic cotton t-shirt outdoors",
    "show_in_gallery": true,
    "updated_at": "2026-09-15T16:00:00.000Z"
  }
}

Approve customer photo

POST /v1/customer-photos/{id}/approve

Approve a pending customer photo. Sets status to approved and makes the photo visible on the storefront. Records moderation timestamp and moderator email.

Path parameters

Parameter Type Description
id string Customer photo ID

Request

curl -X POST "https://api.hydrajs.dev/v1/customer-photos/cp_abc123/approve" \
  -H "Authorization: Bearer sk_YOUR_KEY"

Response 200

{
  "data": {
    "id": "cp_abc123",
    "status": "approved",
    "moderated_at": "2026-09-15T15:00:00.000Z",
    "moderated_by": "admin@store.com",
    "updated_at": "2026-09-15T15:00:00.000Z"
  }
}

Reject customer photo

POST /v1/customer-photos/{id}/reject

Reject a pending customer photo. Sets status to rejected and hides the photo from the storefront. Optionally include rejection notes for internal reference.

Path parameters

Parameter Type Description
id string Customer photo ID

Request body

Field Type Description
notes string Rejection notes (e.g. “Product not visible in photo”)

Request

curl -X POST "https://api.hydrajs.dev/v1/customer-photos/cp_abc123/reject" \
  -H "Authorization: Bearer sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notes": "Product not clearly visible in the photo"
  }'

Response 200

{
  "data": {
    "id": "cp_abc123",
    "status": "rejected",
    "moderated_at": "2026-09-15T15:00:00.000Z",
    "moderated_by": "admin@store.com",
    "moderation_notes": "Product not clearly visible in the photo",
    "updated_at": "2026-09-15T15:00:00.000Z"
  }
}

Replace customer photo image

POST /v1/customer-photos/{id}/replace-image

Replace the image file on an existing customer photo while preserving all metadata, approval status, and moderation history. Useful when a merchant wants to retouch, crop, or color-correct an image without losing the existing record.

Path parameters

Parameter Type Description
id string Customer photo ID

Request body (multipart/form-data)

Field Type Required Description
image file Yes New image file (JPEG, PNG, WebP, GIF, AVIF, max 10MB)
alt_text string No Updated alt text for the new image (max 500 chars)

Request

curl -X POST "https://api.hydrajs.dev/v1/customer-photos/cp_abc123/replace-image" \
  -H "Authorization: Bearer sk_YOUR_KEY" \
  -F "image=@retouched-photo.jpg"

Response 200

{
  "data": {
    "id": "cp_abc123",
    "product_id": "prod_xyz789",
    "image_url": "https://cdn.hydrajs.dev/store_abc/ugc/e5f6g7h8.webp",
    "width": 1200,
    "height": 1500,
    "aspect_ratio": "4:5",
    "file_size": 198432,
    "credit": "@janedoe",
    "status": "approved",
    "updated_at": "2026-09-30T12:00:00.000Z"
  }
}

Delete customer photo

DELETE /v1/customer-photos/{id}

Soft delete a customer photo. The photo is marked as deleted but the image file remains in storage. Deleted photos are hidden from all listings and the storefront.

Path parameters

Parameter Type Description
id string Customer photo ID

Request

curl -X DELETE "https://api.hydrajs.dev/v1/customer-photos/cp_abc123" \
  -H "Authorization: Bearer sk_YOUR_KEY"

Response 204

No response body.


Get approved product photos

GET /v1/products/{id}/customer-photos

Get approved customer photos for a product (storefront-safe endpoint). Returns up to 24 approved photos for display on product pages. Works with publishable or secret keys.

Path parameters

Parameter Type Description
id string Product ID

Request

curl "https://api.hydrajs.dev/v1/products/prod_xyz789/customer-photos" \
  -H "Authorization: Bearer pk_YOUR_KEY"

Response 200

{
  "data": [
    {
      "id": "cp_abc123",
      "product_id": "prod_xyz789",
      "image_url": "https://cdn.hydrajs.dev/store_abc/ugc/a1b2c3d4.jpg",
      "width": 1200,
      "height": 1500,
      "aspect_ratio": "4:5",
      "credit": "@janedoe",
      "link_url": "https://instagram.com/janedoe",
      "link_text": "View on Instagram",
      "alt_text": "Customer wearing navy organic cotton t-shirt",
      "show_in_gallery": true,
      "created_at": "2026-09-15T14:30:00.000Z"
    }
  ]
}

The customer photo object

Field Type Description
id string Unique ID (prefix: cp_)
product_id string Product ID the photo is attached to
image_url string CDN URL of the uploaded image
bucket_path string R2 storage path (internal)
width integer Image width in pixels
height integer Image height in pixels
aspect_ratio string Auto-detected aspect ratio: 1:1 or 4:5
file_size integer File size in bytes
credit string|null Photo credit (name or @handle)
link_url string|null External link URL (e.g. Instagram profile)
link_text string|null Link text display
customer_id string|null Customer ID if submitted by registered customer
customer_email string|null Customer email (for guest submissions)
alt_text string|null Image alt text for accessibility
show_in_gallery boolean Whether to show in product gallery (default true)
status string Moderation status: pending, approved, rejected
moderated_at string|null ISO 8601 timestamp when moderated
moderated_by string|null Email of admin who moderated the photo
moderation_notes string|null Internal rejection notes
created_at string ISO 8601 timestamp when photo was uploaded
updated_at string ISO 8601 timestamp of last update