Skip to content

Photos

Available to: Owner, Project Manager, Supervisor, Badge Worker (assigned jobs only)

Photos can be attached to bids and jobs to document site conditions, progress, materials, and any other visual record relevant to the project.


Where Photos Can Be Attached

Photos are supported on two entity types:

  • Bids — to document pre-work site conditions, measurements, or supporting materials.
  • Jobs — to document work in progress, completion state, or issues encountered.

Photos are not currently supported on invoices or updates directly. To attach a photo to an update, post it as a job or bid photo alongside the update.


Upload Constraints

Constraint Limit
Allowed file types JPEG, PNG, WEBP, HEIC
Maximum file size 15 MB per file
Maximum files per upload 10 files per request

Uploads exceeding 15 MB per file or 10 files per request will be rejected with a validation error. Split large batches into multiple uploads.


HEIC Auto-Conversion

HEIC is the default format for photos taken on modern iPhones. Hardhat Flow accepts HEIC files but converts them automatically:

  1. The original HEIC file is uploaded to DigitalOcean Spaces.
  2. A Celery background task picks up the conversion job.
  3. The file is converted to JPEG and stored, replacing the reference on the photo record.
  4. The original HEIC file is removed from storage after successful conversion.

HEIC conversion is asynchronous. The photo will show as processing briefly after upload. Refresh to see the converted JPEG.


Thumbnail Generation

For every uploaded photo (after any HEIC conversion), a thumbnail is generated asynchronously:

  • Thumbnails are 400px wide, with height proportional to the original aspect ratio.
  • Generated by a Celery worker task — not during the upload request.
  • Stored in DigitalOcean Spaces alongside the full-resolution file.
  • Used in list views and preview panels to reduce page load times.

Full-resolution files are always preserved. Thumbnails are display-only.


Who Can Upload

Role Can Upload to Bids Can Upload to Jobs
Owner Yes — any bid Yes — any job
Project Manager Yes — their assigned bids Yes — their assigned jobs
Supervisor No Yes — their assigned jobs
Badge Worker No Yes — jobs they are assigned to
Accounting No No

Who Can Delete

Photo deletion is controlled by authorship and role:

  • Authors may delete their own photos at any time.
  • Owners may delete any photo regardless of who uploaded it.
  • No other role can delete photos.

All photo deletes in Hardhat Flow are soft deletes. The file is NOT removed from DigitalOcean Spaces — only the database record is flagged as deleted. Only Platform Admin can initiate soft-deletes.


Soft Delete Behavior

When a photo is soft-deleted:

  1. The photo record in the database is flagged with a deleted_at timestamp.
  2. The record no longer appears in API responses or the UI.
  3. The file itself remains in DigitalOcean Spaces and is not purged.
  4. The deletion is recorded in the ActivityLog.

This approach ensures that photos can be recovered if a delete was made in error, and that storage audits remain accurate.

Physical file removal from DigitalOcean Spaces requires Platform Admin action and is handled outside the normal application workflow.


API Reference

List Photos

GET /{tenant-slug}/api/v1/photos/{bids|jobs}/{entity-id}/

Returns a paginated list of non-deleted photos for the given entity.

Response envelope:

{
  "count": 3,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "uuid",
      "entity_type": "job",
      "entity_id": "uuid",
      "uploaded_by_id": "uuid",
      "uploaded_by_name": "Jane Doe",
      "file_url": "https://...",
      "thumbnail_url": "https://...",
      "created_at": "2026-04-07T10:00:00Z"
    }
  ]
}

thumbnail_url will be an empty string immediately after upload and populated once the Celery task completes.


Upload Photos

POST /{tenant-slug}/api/v1/photos/{bids|jobs}/{entity-id}/
Content-Type: multipart/form-data

Field: photos — one or more files (max 10 per request).

Validation errors (400):

Condition Error message
No files provided At least one file is required.
More than 10 files Maximum 10 files per upload request.
Unsupported file type Unsupported file type '...'. Allowed: JPEG, PNG, WEBP, HEIC.
File exceeds 15 MB 'filename' exceeds the 15 MB file size limit.

Success (201): Array of created photo objects (same shape as list results).


Delete a Photo

DELETE /{tenant-slug}/api/v1/photos/{photo-id}/

Soft-deletes the photo. The database record is flagged as deleted; the file in DigitalOcean Spaces is preserved.

Response Condition
204 No Content Success
403 Forbidden Caller is not the author, Owner, or Platform Admin
404 Not Found Photo does not exist or is already deleted

Storage Layout

Files are stored in DigitalOcean Spaces under the following path structure:

/{tenant-slug}/{entity-type}/{entity-id}/{photo-uuid}.{ext}

Thumbnails are stored at:

/{tenant-slug}/{entity-type}/{entity-id}/thumbnails/{photo-uuid}.jpg