Photos

Photo and Video Management

Endpoints for managing photos and videos within RoofLink. Photos can be attached to jobs and to individual line items across estimates, work orders, supplements, and change orders. Each photo record is created through a two-step upload flow: the first API call creates the record and returns a presigned S3 URL, the client uploads the file directly to S3, and the second API call finalizes the record. Files are stored directly in S3-compatible object storage.

Supported Photo Operations

Querying Photos

  • List Photos: Retrieve a paginated list of photos with filtering options by job, line item, and related object.
  • Retrieve Photo: Get full details for a specific photo, including EXIF metadata.

Creating and Managing Photos

  • Create Photo: Step 1 of 2 in the upload flow. Creates a photo record and returns a presigned S3 URL for direct file upload.
  • Mark Photo as Uploaded: Step 2 of 2 in the upload flow. Finalizes the photo record after the file has been uploaded to S3.
  • Update Photo: Update the display name, description, and tags of an existing photo.
  • Delete Photo: Permanently delete a photo, or unrelate it from a specific inspection line item without deleting it.

Photo Upload Flow

Creating a photo requires two API calls with a direct S3 upload in between. The diagram below illustrates the full sequence.

sequenceDiagram
    participant Client
    participant API as RoofLink API
    participant S3 as S3 Compatible Storage

    Client->>API: POST /light/photos/
    Note right of Client: Provide name, optional job,<br/>line items, tags, and EXIF data
    API-->>Client: 201 Created<br/>{ id, upload_url: { url, fields } }

    Client->>S3: POST upload_url.url (multipart/form-data)
    Note right of Client: Include all upload_url.fields<br/>as form fields, plus the file
    S3-->>Client: 204 No Content

    Client->>API: PUT /light/photos/{id}/uploaded/
    API-->>Client: 200 OK — full photo record
    Note left of API: Photo is now visible<br/>to other users in the company

Step 1 of 2 — Create the photo record and upload the file

Call POST /light/photos/ with the photo metadata. The server creates the record and returns a presigned POST object containing:

  • upload_url.url — the S3 endpoint to upload to
  • upload_url.fields — key-value pairs that must be included as form fields in the upload request

The fields.key value contains the S3 object path. The file name is derived from the photo's display name with a random suffix appended to guarantee uniqueness.

Once you have the presigned URL, send a multipart/form-data POST request directly to upload_url.url. Include all entries from upload_url.fields as form fields, and add the file content in a field named file. No server-side proxy is involved in this step.

Step 2 of 2 — Finalize the upload

Call PUT /light/photos/{id}/uploaded/ to mark the photo as uploaded. This makes the photo visible to other users in the company. If the photo is associated with a job that has no cover photo set, this action also sets the photo as the job's cover photo.

Calling the finalize endpoint before the file upload is complete will result in a photo record with a broken URL.

Associating Photos with Jobs and Line Items

A photo can be associated with a job and with line items of various types at creation time. All referenced IDs must belong to the same company as the authenticated user.

FieldTypeDescription
jobintegerAssociates the photo with a job
job_inspection_lineitemsintegerLinks the photo to a job inspection line item
estimate_lineitemsintegerLinks the photo to an estimate line item
supplineitemsintegerLinks the photo to a supplemental line item
change_order_lineitemsintegerLinks the photo to a change order line item
related_tostring[]List of related object identifiers (e.g. Job101)

EXIF Metadata and Geolocation

Photos accept EXIF metadata submitted by the client at upload time. The structure is flexible and varies by device and camera model. When location data is included, the server will attempt to geolocate the photo.

The timestamp key, when present, must be a Unix timestamp in milliseconds and is used to set the photo capture date.

Deleting and Unrelating Photos

The Delete Photo endpoint has two distinct behaviours depending on whether the job_inspection_lineitems query parameter is provided:

  • Without job_inspection_lineitems: The photo record and its associated files are permanently deleted. Returns 204 No Content.
  • With job_inspection_lineitems: The photo is unrelated from the specified inspection line item without being deleted. The line item's last generated report is also invalidated. Returns 200 true.

Pagination

Photo lists are paginated with:

  • Default page size: 10 items
  • Maximum page size: 10 items
  • Results include pagination metadata: count, from_index, to_index, next, previous, and next_page. See the responses page for details.