Skip to content

Field Service (FSM) Jobs API

Manage field service orders (installation/service jobs) via the REST API.

Overview

Operation Method Endpoint
Create POST /restapi/1.0/object/fsm.order
List/Search GET /restapi/1.0/object/fsm.order
Read GET /restapi/1.0/object/fsm.order/{id}
Update PUT /restapi/1.0/object/fsm.order/{id}
Delete DELETE /restapi/1.0/object/fsm.order/{id}

Authentication

X-API-Key: your_api_key_here

FSM Order Fields

Core Fields

Field Type Required Description
name String Auto Order reference (FSM00001)
partner_id Integer Yes Customer ID
location_id Integer No Service location ID
sale_order_id Integer No Linked sales order
scheduled_date_start Datetime No Scheduled start
scheduled_date_end Datetime No Scheduled end
date_start Datetime No Actual start
date_end Datetime No Actual end
stage_id Integer No Workflow stage
person_id Integer No Assigned technician
team_id Integer No Service team
priority String No Priority level
description Text No Job description

Job Type Fields

Field Type Description
type String Job type (custom field)
fsm_type String FSM type identifier

Address Fields (from Location)

Field Type Description
street String Street address
city String City
state_id Integer State ID
zip String ZIP code

Computed Fields

Field Type Description
duration Float Planned duration (hours)
effective_duration Float Actual duration (hours)

FSM Stages (JDX Workflow)

Stage Description
New New job created
Scheduled Job scheduled
In Progress Work started
Completed Work finished
Cancelled Job cancelled

API Examples

Create a Job

curl -X POST "https://your-odoo.com/restapi/1.0/object/fsm.order" \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "partner_id": 123,
    "sale_order_id": 456,
    "scheduled_date_start": "2025-12-15 09:00:00",
    "scheduled_date_end": "2025-12-15 12:00:00",
    "person_id": 10,
    "description": "Install motorized blinds - living room and bedroom"
  }'

Get Jobs by Stage

# Get all jobs in "Scheduled" stage
curl -X GET "https://your-odoo.com/restapi/1.0/object/fsm.order" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "domain=[('stage_id','=',2)]" \
  --data-urlencode "fields=['name','partner_id','scheduled_date_start','person_id','location_id']" \
  --data-urlencode "limit=50" \
  --data-urlencode "order=scheduled_date_start"

Get Today's Jobs

curl -X GET "https://your-odoo.com/restapi/1.0/object/fsm.order" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "domain=[('scheduled_date_start','>=','2025-12-13 00:00:00'),('scheduled_date_start','<=','2025-12-13 23:59:59')]" \
  --data-urlencode "fields=['name','partner_id','scheduled_date_start','stage_id','person_id']" \
  --data-urlencode "order=scheduled_date_start"

Get Jobs by Technician

curl -X GET "https://your-odoo.com/restapi/1.0/object/fsm.order" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "domain=[('person_id','=',10),('stage_id','not in',[5])]" \
  --data-urlencode "fields=['name','partner_id','scheduled_date_start','stage_id','location_id']" \
  --data-urlencode "order=scheduled_date_start"

Get Job Detail

curl -X GET "https://your-odoo.com/restapi/1.0/object/fsm.order/789" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "fields=['name','partner_id','location_id','sale_order_id','scheduled_date_start','scheduled_date_end','date_start','date_end','stage_id','person_id','description','duration']"

Update Job Stage

# Move job to "In Progress"
curl -X PUT "https://your-odoo.com/restapi/1.0/object/fsm.order/789" \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "stage_id": 3,
    "date_start": "2025-12-13 09:15:00"
  }'

Complete a Job

curl -X PUT "https://your-odoo.com/restapi/1.0/object/fsm.order/789" \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "stage_id": 4,
    "date_end": "2025-12-13 11:30:00"
  }'

Reschedule Job

curl -X PUT "https://your-odoo.com/restapi/1.0/object/fsm.order/789" \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "scheduled_date_start": "2025-12-16 14:00:00",
    "scheduled_date_end": "2025-12-16 17:00:00"
  }'

Reassign Technician

curl -X PUT "https://your-odoo.com/restapi/1.0/object/fsm.order/789" \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "person_id": 15
  }'

FSM Stages

curl -X GET "https://your-odoo.com/restapi/1.0/object/fsm.stage" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "fields=['name','sequence','is_closed']" \
  --data-urlencode "order=sequence"

Service Locations

curl -X GET "https://your-odoo.com/restapi/1.0/object/fsm.location" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "domain=[('partner_id','=',123)]" \
  --data-urlencode "fields=['name','street','city','state_id','zip']"

Technicians (FSM Persons)

curl -X GET "https://your-odoo.com/restapi/1.0/object/fsm.person" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "fields=['name','partner_id','user_id']"

Linked Sale Order

# Get sale order linked to FSM job
curl -X GET "https://your-odoo.com/restapi/1.0/object/sale.order/456" \
  -H "X-API-Key: your_api_key" \
  -G \
  --data-urlencode "fields=['name','partner_id','amount_total','order_line']"

Domain Filter Examples

# Active jobs (not cancelled/completed)
[('stage_id', 'not in', [4, 5])]

# Today's jobs
[('scheduled_date_start', '>=', '2025-12-13 00:00:00'),
 ('scheduled_date_start', '<=', '2025-12-13 23:59:59')]

# This week's jobs
[('scheduled_date_start', '>=', '2025-12-09'),
 ('scheduled_date_start', '<=', '2025-12-15')]

# By technician
[('person_id', '=', 10)]

# Unassigned jobs
[('person_id', '=', False)]

# By customer
[('partner_id', '=', 123)]

# Installation jobs (custom type)
[('type', '=', 'install')]

# Service jobs (custom type)
[('type', '=', 'service')]

# Overdue jobs
[('scheduled_date_start', '<', '2025-12-13'),
 ('stage_id', 'not in', [4, 5])]

# Linked to specific sale order
[('sale_order_id', '=', 456)]

Python Client Example

import requests
from datetime import datetime, timedelta

class FSMJobClient:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip('/')
        self.headers = {
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        }

    def create_job(
        self,
        partner_id: int,
        scheduled_start: str,
        scheduled_end: str = None,
        sale_order_id: int = None,
        person_id: int = None,
        description: str = None
    ) -> dict:
        """Create a new FSM job."""
        data = {
            'partner_id': partner_id,
            'scheduled_date_start': scheduled_start
        }
        if scheduled_end:
            data['scheduled_date_end'] = scheduled_end
        if sale_order_id:
            data['sale_order_id'] = sale_order_id
        if person_id:
            data['person_id'] = person_id
        if description:
            data['description'] = description

        response = requests.post(
            f"{self.base_url}/restapi/1.0/object/fsm.order",
            headers=self.headers,
            json=data
        )
        response.raise_for_status()
        return response.json().get('fsm.order', {})

    def get_jobs_by_date(self, date: str) -> list:
        """Get all jobs for a specific date."""
        domain = [
            ('scheduled_date_start', '>=', f'{date} 00:00:00'),
            ('scheduled_date_start', '<=', f'{date} 23:59:59')
        ]
        params = {
            'domain': str(domain),
            'fields': "['name','partner_id','scheduled_date_start','stage_id','person_id','location_id']",
            'order': 'scheduled_date_start'
        }
        response = requests.get(
            f"{self.base_url}/restapi/1.0/object/fsm.order",
            headers=self.headers,
            params=params
        )
        response.raise_for_status()
        return response.json().get('fsm.order', [])

    def get_technician_jobs(self, person_id: int, include_completed: bool = False) -> list:
        """Get jobs assigned to a technician."""
        domain = [('person_id', '=', person_id)]
        if not include_completed:
            domain.append(('stage_id', 'not in', [4, 5]))

        params = {
            'domain': str(domain),
            'fields': "['name','partner_id','scheduled_date_start','stage_id','location_id']",
            'order': 'scheduled_date_start'
        }
        response = requests.get(
            f"{self.base_url}/restapi/1.0/object/fsm.order",
            headers=self.headers,
            params=params
        )
        response.raise_for_status()
        return response.json().get('fsm.order', [])

    def update_stage(self, job_id: int, stage_id: int, **kwargs) -> dict:
        """Update job stage with optional timestamp."""
        data = {'stage_id': stage_id, **kwargs}
        response = requests.put(
            f"{self.base_url}/restapi/1.0/object/fsm.order/{job_id}",
            headers=self.headers,
            json=data
        )
        response.raise_for_status()
        return response.json().get('fsm.order', {})

    def start_job(self, job_id: int) -> dict:
        """Start a job (set to In Progress)."""
        return self.update_stage(
            job_id,
            stage_id=3,
            date_start=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        )

    def complete_job(self, job_id: int) -> dict:
        """Complete a job."""
        return self.update_stage(
            job_id,
            stage_id=4,
            date_end=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        )

    def reschedule_job(self, job_id: int, new_start: str, new_end: str = None) -> dict:
        """Reschedule a job to a new date/time."""
        data = {'scheduled_date_start': new_start}
        if new_end:
            data['scheduled_date_end'] = new_end

        response = requests.put(
            f"{self.base_url}/restapi/1.0/object/fsm.order/{job_id}",
            headers=self.headers,
            json=data
        )
        response.raise_for_status()
        return response.json().get('fsm.order', {})

    def get_stages(self) -> list:
        """Get all FSM stages."""
        params = {
            'fields': "['name','sequence','is_closed']",
            'order': 'sequence'
        }
        response = requests.get(
            f"{self.base_url}/restapi/1.0/object/fsm.stage",
            headers=self.headers,
            params=params
        )
        response.raise_for_status()
        return response.json().get('fsm.stage', [])


# Usage
client = FSMJobClient('https://your-odoo.com', 'your_api_key')

# Create installation job
job = client.create_job(
    partner_id=123,
    scheduled_start='2025-12-15 09:00:00',
    scheduled_end='2025-12-15 12:00:00',
    sale_order_id=456,
    person_id=10,
    description='Install motorized blinds'
)
print(f"Created job {job['name']}")

# Get today's schedule
today = datetime.now().strftime('%Y-%m-%d')
jobs = client.get_jobs_by_date(today)
print(f"Today: {len(jobs)} jobs scheduled")

# Start a job
client.start_job(job['id'])

# Complete the job
client.complete_job(job['id'])

Dashboard Stats Example

def get_fsm_dashboard(client: FSMJobClient) -> dict:
    """Get FSM dashboard statistics."""
    today = datetime.now().strftime('%Y-%m-%d')

    # Count by stage
    stages = client.get_stages()
    stage_counts = {}
    for stage in stages:
        params = {
            'domain': str([('stage_id', '=', stage['id'])]),
            'fields': "['id']"
        }
        response = requests.get(
            f"{client.base_url}/restapi/1.0/object/fsm.order",
            headers=client.headers,
            params=params
        )
        jobs = response.json().get('fsm.order', [])
        stage_counts[stage['name']] = len(jobs)

    # Today's jobs
    today_jobs = client.get_jobs_by_date(today)

    return {
        'stage_counts': stage_counts,
        'today_count': len(today_jobs),
        'today_jobs': today_jobs
    }

Notes

  • Jobs are typically created from sale orders when using MTO (Make to Order) workflow
  • scheduled_date_start and scheduled_date_end define planned schedule
  • date_start and date_end record actual work times
  • Use person_id to assign technicians
  • location_id links to customer's service address (fsm.location)
  • Stage changes trigger workflow automation (activities, notifications)