Work orders

Lists and gets work orders for the current tenant; create, transition status, complete, log time; list attachments and update attachment metadata. Set tenant context before calling these methods.

List work orders

List all work orders for the current tenant (from v_work_orders).

client.workOrders.list()

await client.setTenant(tenantId)
// Refresh session so JWT carries tenant_id, then:
const workOrders = await client.workOrders.list()
// Returns WorkOrderRow[]

Get by ID

Fetch a single work order by ID.

client.workOrders.getById(id)

const workOrder = await client.workOrders.getById('uuid-of-work-order')
// Returns WorkOrderRow | null

Create work order

Create a new work order. Returns the new work order UUID.

client.workOrders.create(params)

const workOrderId = await client.workOrders.create({
  tenantId: 'uuid-of-tenant',
  title: 'Repair HVAC unit',
  description: 'Inspect and repair unit 3B',
  priority: 'medium',
  maintenanceType: 'corrective',
  assignedTo: 'uuid-of-user',
  locationId: 'uuid-of-location',
  assetId: 'uuid-of-asset',
  dueDate: '2025-03-01',
  pmScheduleId: null,
  projectId: null, // optional, for cost roll-up by project
})
// Returns string (UUID)
  • Name
    tenantId
    Description

    Required. Tenant UUID.

  • Name
    title
    Description

    Required. Work order title.

  • Name
    description
    Description

    Optional. Description.

  • Name
    priority
    Description

    Optional. Default medium. Priority key.

  • Name
    maintenanceType
    Description

    Optional. Maintenance type key.

  • Name
    assignedTo
    Description

    Optional. Assignee user UUID.

  • Name
    locationId
    Description

    Optional. Location UUID.

  • Name
    assetId
    Description

    Optional. Asset UUID.

  • Name
    dueDate
    Description

    Optional. ISO date string.

  • Name
    pmScheduleId
    Description

    Optional. PM schedule UUID.

  • Name
    projectId
    Description

    Optional. Project UUID for cost roll-up by project. See Projects and Costs & lifecycle.

Transition status

Move a work order to a new status (e.g. from open to in_progress).

client.workOrders.transitionStatus(params)

await client.workOrders.transitionStatus({
  tenantId: 'uuid-of-tenant',
  workOrderId: 'uuid-of-work-order',
  toStatusKey: 'in_progress',
})

Complete work order

Mark a work order as completed (transition to completed status). You can optionally record cause and resolution when completing.

client.workOrders.complete(params)

await client.workOrders.complete({
  tenantId: 'uuid-of-tenant',
  workOrderId: 'uuid-of-work-order',
  cause: 'Worn bearing caused vibration',
  resolution: 'Replaced bearing and realigned motor',
})

Log time

Log time spent on a work order. Returns the time entry UUID.

client.workOrders.logTime(params)

const timeEntryId = await client.workOrders.logTime({
  tenantId: 'uuid-of-tenant',
  workOrderId: 'uuid-of-work-order',
  minutes: 60,
  entryDate: '2025-02-03',
  userId: null,
  description: 'Replaced filter',
})
// Returns string (UUID)
  • Name
    tenantId
    Description

    Required. Tenant UUID.

  • Name
    workOrderId
    Description

    Required. Work order UUID.

  • Name
    minutes
    Description

    Required. Number of minutes.

  • Name
    entryDate
    Description

    Optional. ISO date string.

  • Name
    userId
    Description

    Optional. User UUID. Defaults to current user.

  • Name
    description
    Description

    Optional. Work description.

Attachments overview

Attachments use Supabase Storage and a database trigger: there is no RPC to “add” an attachment. You upload a file to the attachments bucket with path tenant_id/work_order/work_order_id/filename (three folder segments before the filename). A trigger creates app.files and app.entity_attachments. The SDK provides list attachments and update attachment metadata; use Storage for upload and signed URLs.

ActionSDK / API
Create attachmentclient.supabase.storage.from('attachments').upload(path, file, opts). Path: tenant_id/work_order/work_order_id/uuid_filename; trigger creates records.
List attachmentsclient.workOrders.listAttachments(workOrderId). Returns WorkOrderAttachmentRow[] with id, bucket_id, storage_path, filename, label, kind, etc.
Get file URLclient.supabase.storage.from(row.bucket_id).createSignedUrl(row.storage_path, expiresIn).
Update label/kindclient.workOrders.updateAttachmentMetadata({ attachmentId, label?, kind? }).

Set tenant context with client.setTenant(tenantId) before listing or uploading.

Upload attachment

Upload the file to the attachments bucket. The path must be {tenantId}/work_order/{workOrderId}/{uuid}_{filename} (folder segments are tenant, entity type, entity id). Optional metadata (tenant_id, entity_type, entity_id, label, kind, document fields) may be passed if your Supabase version supports it; otherwise set label/kind later via rpc_update_entity_attachment_metadata.

Upload to attachments bucket

await client.setTenant(tenantId)

const path = `${tenantId}/work_order/${workOrderId}/${crypto.randomUUID()}_${file.name}`
const { data, error } = await client.supabase.storage
  .from('attachments')
  .upload(path, file, {
    contentType: file.type ?? 'application/octet-stream',
    upsert: false,
    metadata: {
      tenant_id: tenantId,
      entity_type: 'work_order',
      entity_id: workOrderId,
      label: 'Before photo',  // optional
      kind: 'photo',          // optional
    },
  })
if (error) throw error
// Trigger creates app.files + entity_attachments; no attachment UUID returned from upload.
// To get the new attachment id, list v_work_order_attachments for this work_order_id and take the latest.

The uploader must be a tenant member and either assigned to the work order or have workorder.edit permission; otherwise Storage RLS rejects the upload.

List attachments

List attachments for a work order. Returns rows with id, work_order_id, file_id, bucket_id, storage_path, filename, label, kind, created_by_name, created_at. Use bucket_id and storage_path with createSignedUrl (see next section).

client.workOrders.listAttachments(workOrderId)

await client.setTenant(tenantId)

const attachments = await client.workOrders.listAttachments(workOrderId)
// Returns WorkOrderAttachmentRow[] (ordered by created_at desc)
// Use row.bucket_id and row.storage_path for createSignedUrl.

Raw: query v_work_order_attachments

await client.setTenant(tenantId)

const { data: attachments, error } = await client.supabase
  .from('v_work_order_attachments')
  .select('id, work_order_id, file_id, bucket_id, storage_path, filename, label, kind, created_by_name, created_at')
  .eq('work_order_id', workOrderId)
  .order('created_at', { ascending: false })

if (error) throw error

Get signed URL

Attachments are stored in a private bucket. Do not store permanent URLs. For each row from listAttachments, call createSignedUrl with bucket_id and storage_path to get a short-lived URL (e.g. 1 hour). RLS on storage.objects is evaluated when creating the signed URL; only users who can read the linked work order receive a URL.

Create signed URL for an attachment row

// row is a WorkOrderAttachmentRow from client.workOrders.listAttachments(workOrderId)
const { data, error } = await client.supabase.storage
  .from(row.bucket_id)
  .createSignedUrl(row.storage_path, 3600)  // 3600 seconds = 1 hour

if (error) throw error
// data.signedUrl: use for display or download

Update attachment metadata

Set or change the label and/or kind after upload (e.g. when Storage metadata was not used). The caller must be a tenant member and either the attachment creator or an admin/manager. Get attachmentId from client.workOrders.listAttachments(workOrderId) (row id).

client.workOrders.updateAttachmentMetadata(params)

await client.workOrders.updateAttachmentMetadata({
  attachmentId: 'uuid-from-list-attachments',
  label: 'Before photo',
  kind: 'photo',
})

Raw: rpc_update_entity_attachment_metadata

await client.supabase.rpc('rpc_update_entity_attachment_metadata', {
  p_attachment_id: attachmentId,
  p_label: 'Before photo',
  p_kind: 'photo',
})
  • Name
    attachmentId
    Description

    Required. Attachment UUID (row id from listAttachments).

  • Name
    label
    Description

    Optional. Label. Max 255 characters.

  • Name
    kind
    Description

    Optional. Kind key (e.g. photo, document); format a-z0-9_.

Was this page helpful?