Skip to main content
All endpoints require a valid Bearer token and the api.jobs.manage.manage permission unless noted. State-mutating requests (POST, PUT, DELETE) must include a csrf_token field — obtain one from GET /api/security/csrf-token.
Job fields containing personal contact information (private_contact_name, private_contact_email, access_details) are encrypted at rest using XSalsa20-Poly1305 and are transparently decrypted in API responses.

Jobs

List and retrieve jobs

GET /api/jobs/manage?action=listReturns a paginated list of jobs. You can narrow results with any combination of filters.Required permission: api.jobs.manage.list
action
string
required
Must be list.
Full-text search across job title, reference, and description.
status_id
integer
Filter by job status ID.
priority_id
integer
Filter by priority ID.
agent_id
integer
Filter by assigned agent.
contractor_id
integer
Filter by assigned contractor.
created_by
integer
Filter by the staff member who created the job.
has_photos
string
Set to true to return only jobs that have photos attached.
has_documents
string
Set to true to return only jobs that have documents attached.
limit
integer
default:"50"
Maximum records to return (max 200).
offset
integer
default:"0"
Number of records to skip.
curl -X GET "https://propops.yourcompany.com/api/jobs/manage?action=list&status_id=1&limit=10" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": [
    {
      "ID": 1001,
      "uuid": "550e8400-e29b-41d4-a716-446655440001",
      "job_ref": "JOB-001",
      "job_title": "Boiler Service - Unit 001",
      "job_description": "Annual boiler service required for property unit 001.",
      "status_id": 1,
      "status_name": "Open",
      "priority_id": 2,
      "priority_name": "Medium",
      "job_type_id": 3,
      "job_type_name": "Gas & Heating",
      "agent_id": 10,
      "agent_name": "Agent_001",
      "contractor_id": 20,
      "contractor_name": "Contractor_001",
      "full_address": "1 Example Street, Sample Town, EX1 1AA",
      "job_total_budget": 250.00,
      "contractor_budget": 180.00,
      "vat_percentage": 20.0,
      "total_amount": 216.00,
      "date_works_start_date": "2024-06-01",
      "date_works_end_date": "2024-06-02",
      "photos_count": 3,
      "documents_count": 1,
      "is_pinned": false,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-06-01T14:00:00Z"
    }
  ],
  "count": 42,
  "message": "Operation completed successfully"
}

Create a job

POST /api/jobs/manage Creates a new maintenance job. On success returns HTTP 201 with the new job ID, UUID, and reference. Required permission: api.jobs.manage.manage
action
string
required
Must be create.
job_title
string
required
Short descriptive title for the job.
job_description
string
required
Full description of the works required.
priority_id
integer
required
Priority level ID. Retrieve available values from GET /api/jobs/priorities?action=list.
status_id
integer
required
Initial job status ID. Retrieve available values from GET /api/jobs/statuses?action=list.
job_type_id
integer
required
Job type category ID. Retrieve available values from GET /api/jobs/types?action=list.
address_id
integer
required
Property address ID.
agent_id
integer
ID of the agent to assign.
contractor_id
integer
ID of the contractor to assign.
tenants_ids
array
Array of tenant IDs associated with the job.
job_total_budget
number
Total client-facing budget (inclusive of VAT).
contractor_budget
number
Amount payable to the contractor.
vat_percentage
number
default:"20.0"
VAT rate to apply. Defaults to the platform VAT rate configured by your administrator (default 20%).
date_works_start_date
string
Scheduled start date (YYYY-MM-DD).
date_works_end_date
string
Scheduled completion date (YYYY-MM-DD).
access_details
string
Site access instructions. Encrypted at rest.
works_order_ref
string
Works order reference number.
invoice_no
string
Invoice number to pre-populate.
private_contact_name
string
Private contact name. Encrypted at rest.
private_contact_email
string
Private contact email. Encrypted at rest.
csrf_token
string
required
CSRF token from GET /api/security/csrf-token.
curl -X POST "https://propops.yourcompany.com/api/jobs/manage" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "action=create&job_title=Boiler+Service+-+Unit+001&job_description=Annual+boiler+service&priority_id=2&status_id=1&job_type_id=3&address_id=5&contractor_id=20&vat_percentage=20.0&job_total_budget=250.00&csrf_token=<csrf-token>"
{
  "success": true,
  "job_id": 1001,
  "job_uuid": "550e8400-e29b-41d4-a716-446655440001",
  "job_ref": "JOB-001",
  "message": "Job created successfully"
}

Update a job

POST /api/jobs/manage with action=update Update one or more fields on an existing job. Only the fields you include in the request body are changed.
You cannot update archived jobs. Archived jobs return HTTP 403.
action
string
required
Must be update.
uuid
string
required
UUID of the job to update.
csrf_token
string
required
CSRF token.
All create-time fields are also accepted as optional update fields. Omit any field you do not want to change.

Delete a job

POST /api/jobs/manage with action=delete Permanently removes a job and all associated data (case notes, photos, documents).
action
string
required
Must be delete.
uuid
string
required
UUID of the job to delete.
csrf_token
string
required
CSRF token.

Case Notes

Case notes support rich text, email threading, and file attachments. Note text is encrypted at rest.

List case notes

GET /api/jobs/case-notes?action=list&job_uuid=<uuid> Required permission: api.jobs.case_notes.manage
action
string
required
Must be list.
job_uuid
string
required
UUID of the parent job.
curl -X GET "https://propops.yourcompany.com/api/jobs/case-notes?action=list&job_uuid=550e8400-e29b-41d4-a716-446655440001" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": [
    {
      "ID": 501,
      "uuid": "550e8400-e29b-41d4-a716-446655440050",
      "job_id": 1001,
      "message": "Contractor attended site and completed boiler service.",
      "created_by_name": "Staff_001",
      "send_email": false,
      "attachments_count": 2,
      "created_at": "2024-06-01T14:00:00Z"
    }
  ],
  "count": 5,
  "message": "Operation completed successfully"
}

Create a case note

POST /api/jobs/case-notes
job_uuid
string
required
UUID of the parent job.
message
string
required
Note content. Accepts plain text or HTML from rich-text editors.
send_email
boolean
default:"false"
When true, sends the note as an email to relevant parties. Replies to that email will be threaded back to this note.
csrf_token
string
required
CSRF token.

Update a case note

POST /api/jobs/case-notes with action=update — Edit the note text or toggle the pinned state.

Delete a case note

POST /api/jobs/case-notes with action=delete — Remove a case note by UUID.

Photos

List photos

GET /api/jobs/photos?job_id=<id> Required permission: api.jobs.photos.manage Returns photo metadata grouped by type (before, during, after, other).

Upload a photo

POST /api/jobs/photos with action=upload Upload via multipart/form-data. Photos are auto-compressed on ingest.
action
string
required
Must be upload.
job_id
integer
required
ID of the job.
file
file
required
Image or video file. Accepted formats: JPEG, PNG, MP4. Maximum 20 MB per file.
csrf_token
string
required
CSRF token.

Delete a photo

DELETE /api/jobs/photos — Send the photo ID as a JSON body {"id": <id>}.

Documents

List documents

GET /api/jobs/documents?action=list&job_id=<id> Required permission: api.jobs.documents.manage Returns document metadata. Download a document with action=download&uuid=<uuid>.

Upload a document

POST /api/jobs/documents with action=upload
action
string
required
Must be upload.
job_id
integer
required
ID of the job.
file_type_id
integer
required
File type category ID.
file
file
required
Document file. Accepted formats: PDF, DOCX, JPEG, PNG. Maximum 20 MB.
csrf_token
string
required
CSRF token.

Delete a document

DELETE /api/jobs/documents — Send the document ID as a JSON body {"document_id": <id>}.

Recalls

A recall reopens a completed job, recording the reason and creating a full audit trail.

Create a recall

POST /api/jobs/recall Required permission: api.jobs.recall.manage
job_uuid
string
required
UUID of the job to mark as recalled.
recall_reason
string
required
Explanation of why the recall is being raised.
is_recall
integer
default:"1"
Set to 1 to mark as recalled, 0 to complete/clear a recall.
action_type
string
default:"standard"
When completing a recall (is_recall=0): standard to complete, cancel to cancel.
csrf_token
string
required
CSRF token.
curl -X POST "https://propops.yourcompany.com/api/jobs/recall" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "job_uuid=550e8400-e29b-41d4-a716-446655440001&recall_reason=Repair+was+unsuccessful&csrf_token=<csrf-token>"
{
  "success": true,
  "message": "Job marked as recalled successfully",
  "job_id": 1001,
  "job_ref": "JOB-001",
  "is_recall": 1,
  "action": "marked as recalled"
}

Amendments

The amendment flow lets contractors or agents propose changes to job fields (e.g. budget) which must be accepted or rejected by a manager.

List amendment requests

GET /api/jobs/amend-request Returns pending or historical amendment requests. Filter by job, status, or retrieve a single request by UUID. Required permission: api.jobs.amend_request.manage
job_uuid
string
Filter requests to a specific job UUID.
status
string
Filter by status: pending, approved, or rejected.
uuid
string
Retrieve a single amendment request by its UUID.
curl -X GET "https://propops.yourcompany.com/api/jobs/amend-request?job_uuid=550e8400-e29b-41d4-a716-446655440001&status=pending" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": [
    {
      "uuid": "aa11bb22-cc33-4444-dd55-ee6677889900",
      "job_uuid": "550e8400-e29b-41d4-a716-446655440001",
      "job_ref": "JOB-001",
      "field_name": "contractor_budget",
      "current_value": "180.00",
      "requested_value": "220.00",
      "reason": "Additional materials required.",
      "status": "pending",
      "requested_by": "Contractor_001",
      "requested_at": "2024-06-14T10:00:00Z"
    }
  ],
  "count": 1,
  "message": "Operation completed successfully"
}

Submit an amendment request

POST /api/jobs/amend-request Creates a new amendment request for a job field. Required permission: api.jobs.amend_request.manage
Requires CSRF token.
job_uuid
string
required
UUID of the job.
field_name
string
required
The job field being proposed for change (e.g. contractor_budget). Use GET /api/jobs/amendable-fields to discover eligible fields.
requested_value
string
required
The proposed new value.
reason
string
required
Justification for the change.
csrf_token
string
required
CSRF token from GET /api/security/csrf-token.
curl -X POST "https://propops.yourcompany.com/api/jobs/amend-request" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"job_uuid":"550e8400-e29b-41d4-a716-446655440001","field_name":"contractor_budget","requested_value":"220.00","reason":"Additional materials required.","csrf_token":"<csrf-token>"}'
{
  "success": true,
  "message": "Amendment request submitted"
}

Approve or reject an amendment request

PATCH /api/jobs/amend-request Approves or rejects a pending amendment request. Only users with management authority can action requests. Required permission: api.jobs.amend_request.manage
Requires CSRF token.
uuid
string
required
UUID of the amendment request to action.
status
string
required
New status: approved or rejected.
csrf_token
string
required
CSRF token from GET /api/security/csrf-token.
curl -X PATCH "https://propops.yourcompany.com/api/jobs/amend-request" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"uuid":"aa11bb22-cc33-4444-dd55-ee6677889900","status":"approved","csrf_token":"<csrf-token>"}'
{
  "success": true,
  "message": "Amendment request approved"
}

SLA Status

Get SLA status

GET /api/jobs/sla-status Returns SLA compliance data and breach detection for jobs. Response includes whether each job is within its SLA window, overdue, or breached. Required permission: api.jobs.manage.manage
curl -X GET "https://propops.yourcompany.com/api/jobs/sla-status" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": {
    "jobs_within_sla": 108,
    "jobs_overdue": 12,
    "jobs_breached": 4,
    "breach_details": [
      {
        "job_uuid": "550e8400-e29b-41d4-a716-446655440001",
        "job_ref": "JOB-001",
        "sla_deadline": "2024-06-01T17:00:00Z",
        "hours_overdue": 26
      }
    ]
  },
  "message": "Operation completed successfully"
}

Approval Gate

The approval gate system requires management sign-off before certain jobs can progress to the next status.

Approve or reject a gate

POST /api/jobs/approval-gate Required permission: api.jobs.manage.manage
job_uuid
string
required
UUID of the job at the approval gate.
decision
string
required
approve or reject.
notes
string
Optional notes to attach to the decision.
csrf_token
string
required
CSRF token.
curl -X POST "https://propops.yourcompany.com/api/jobs/approval-gate" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"job_uuid":"550e8400-e29b-41d4-a716-446655440001","decision":"approve","notes":"Checked on site — proceed.","csrf_token":"<csrf-token>"}'
{
  "success": true,
  "message": "Job approved and progressed to next status"
}

Reference Endpoints

These read-only endpoints return lookup data for populating form dropdowns.
EndpointDescription
GET /api/jobs/statuses?action=listAll job status options
GET /api/jobs/types?action=listAll job type categories
GET /api/jobs/priorities?action=listAll priority levels
GET /api/jobs/payment-statuses?action=listPayment status options
GET /api/jobs/time-ranges?action=listAvailable contractor time slots
GET /api/jobs/addresses?action=listSearchable property addresses

Pin / Unpin

Toggle job pin

POST /api/jobs/toggle-pin Toggles the pinned state of a job for the authenticated user. Pinned jobs appear in the Pinned Jobs dashboard widget. Each user maintains their own independent pin list. Required permission: api.jobs.toggle_pin.manage
job_uuid
string
required
UUID of the job to pin or unpin.
curl -X POST "https://propops.yourcompany.com/api/jobs/toggle-pin" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"job_uuid": "550e8400-e29b-41d4-a716-446655440001"}'
{
  "success": true,
  "is_pinned": true,
  "message": "Job pinned to your dashboard"
}
If the job was already pinned, is_pinned will be false and the message will be "Job unpinned from your dashboard".

Remedials

Remedial jobs are linked follow-on jobs created when the original job requires additional work after completion. The remedials API manages those relationships. GET /api/jobs/remedials?action=list&job_uuid=<uuid> Returns all jobs linked as remedials to the specified job. Required permission: api.jobs.remedials.manage
action
string
required
Must be list.
job_uuid
string
required
UUID of the parent job.
curl -X GET "https://propops.yourcompany.com/api/jobs/remedials?action=list&job_uuid=550e8400-e29b-41d4-a716-446655440001" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": [
    {
      "ID": 1055,
      "uuid": "550e8400-e29b-41d4-a716-446655441055",
      "job_ref": "JOB-1055",
      "job_title": "Boiler follow-up — parts replacement",
      "status_name": "Open",
      "created_on": "2024-06-15T09:00:00Z"
    }
  ],
  "count": 1,
  "message": "Operation completed successfully"
}

POST /api/jobs/remedials with action=link Creates a remedial relationship between an existing job and a target job. Required permission: api.jobs.remedials.manage
Requires CSRF token.
action
string
required
Must be link.
job_uuid
string
required
UUID of the parent job.
remedials_job_uuid
string
required
UUID of the job to link as a remedial.
csrf_token
string
required
CSRF token from GET /api/security/csrf-token.
curl -X POST "https://propops.yourcompany.com/api/jobs/remedials" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "link",
    "job_uuid": "550e8400-e29b-41d4-a716-446655440001",
    "remedials_job_uuid": "550e8400-e29b-41d4-a716-446655441055",
    "csrf_token": "<csrf-token>"
  }'
{
  "success": true,
  "message": "Remedial job linked"
}

POST /api/jobs/remedials with action=unlink Removes the remedial relationship between two jobs. The jobs themselves are not deleted. Required permission: api.jobs.remedials.manage
Requires CSRF token.
action
string
required
Must be unlink.
job_uuid
string
required
UUID of the job to unlink from its remedial parent.
csrf_token
string
required
CSRF token.

Mark Recall Completed

Complete a recalled job

POST /api/jobs/mark-recall-completed Marks a job that is currently in recall status as completed. This closes the recall cycle and records a completion timestamp. Required permission: api.jobs.mark_recall_completed.manage
job_uuid
string
required
UUID of the recalled job to mark as completed. Send as a JSON body.
curl -X POST "https://propops.yourcompany.com/api/jobs/mark-recall-completed" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"job_uuid": "550e8400-e29b-41d4-a716-446655440001"}'
{
  "success": true,
  "message": "Recall marked as completed successfully",
  "data": {
    "job_id": 1001,
    "job_title": "Boiler Service - Unit 001"
  }
}
Error — job not in recall state:
{
  "success": false,
  "message": "This job is not marked as a recall"
}

Amendable Fields

Get amendable fields

GET /api/jobs/amendable-fields?job_uuid=<uuid> Returns the list of fields that can be proposed for amendment on a specific job, along with their current values and available options. The response is role-aware — contractors see a different subset of fields than staff. Required permission: api.jobs.amendable_fields.manage
job_uuid
string
required
UUID of the job to retrieve amendable fields for.
curl -X GET "https://propops.yourcompany.com/api/jobs/amendable-fields?job_uuid=550e8400-e29b-41d4-a716-446655440001" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": {
    "job_uuid": "550e8400-e29b-41d4-a716-446655440001",
    "job_ref": "JOB-001",
    "user_type": "staff",
    "fields": [
      {
        "key": "job_title",
        "label": "Job Title",
        "type": "text",
        "current_value": "Boiler Service - Unit 001",
        "required": true,
        "description": "The title/name of the job"
      },
      {
        "key": "job_description",
        "label": "Job Description",
        "type": "textarea",
        "current_value": "Annual boiler service required.",
        "required": false,
        "description": "Detailed description of the job"
      }
    ]
  }
}

Video Variants

Get video variants for a job photo

GET /api/jobs/video-variants?uuid=<uuid> Returns available resolution variants for adaptive video playback for a specific job photo or video file. Use this to build quality-selection controls in video players. Required permission: api.jobs.photos.view
uuid
string
required
UUID of the job photo or video record to retrieve variants for.
curl -X GET "https://propops.yourcompany.com/api/jobs/video-variants?uuid=a1b2c3d4-e5f6-7890-abcd-ef0123456789" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "uuid": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
  "original": {
    "url": "/attachment/a1b2c3d4-e5f6-7890-abcd-ef0123456789",
    "size": 15728640,
    "path": "uploads/jobs/videos/a1b2c3d4.mp4"
  },
  "variants": {
    "1080p": {
      "url": "/uploads/jobs/videos/variants/a1b2c3d4_1080p.mp4",
      "width": 1920,
      "height": 1080,
      "bitrate": 4000000,
      "size": 12582912
    },
    "720p": {
      "url": "/uploads/jobs/videos/variants/a1b2c3d4_720p.mp4",
      "width": 1280,
      "height": 720,
      "bitrate": 2500000,
      "size": 6291456
    }
  },
  "thumbnail": "/uploads/jobs/videos/thumbnails/a1b2c3d4_thumb.jpg",
  "processing_status": "completed",
  "processed": true,
  "processed_at": "2024-06-01T14:05:00Z"
}
Variants are only available for video files that have been processed. If variants is an empty array the original file is the only available source.