FieldCamp
Resources

Jobs | FieldCamp API

Reference for the FieldCamp Jobs API — create, schedule, filter, and update field service jobs with JSON requests on the v1 endpoints.

The FieldCamp Jobs API is the headline resource for scheduled work: a client, an address, a time window, a team, and one or more line items. If you are building a booking system, a dispatch tool, or a sync to another platform, the /api/v1/jobs endpoints are the primary surface you will integrate against. This page covers the current request shape, filter parameters, status lifecycle, and common patterns for working with job and visit statuses.

What changed in v1

The Jobs API now lives under /api/v1/jobs and accepts a normal application/json body — not multipart/form-data. Earlier internal versions of the docs described a two-field form payload (jobData + notes); that pattern is no longer required. Send a single JSON object and you are done.

If you previously saw 400 jobNumber is required responses despite including jobNumber in your payload, you were almost certainly hitting the old multipart parser. Switching to application/json against /api/v1/jobs resolves it.

Authentication and base URL

All requests use the standard FieldCamp API base URL and your API key. See Authentication for header conventions, Errors for the error envelope, and Rate limits for throttling rules. Idempotent retries are documented in Idempotency.

Most important things to know

POST /api/v1/jobs uses JSON. Set Content-Type: application/json and send the job object directly. Do not stringify a jobData field inside a form payload.

assignedToTeams takes user IDs, not team IDs. Get them from GET /api/v1/team. The field name is historical and is preserved for compatibility — see the Team resource.

Datetimes are UTC ISO-8601. timezone is a display hint for the FieldCamp UI — do not encode the offset into startDateTime. Always convert your local time to UTC before sending.

Create a job

POST /api/v1/jobs accepts a JSON body with the following important fields:

  • type"one-off" or "recurring". One-off jobs run once; recurring jobs spawn child visits on a schedule.
  • clientId — the FieldCamp client this job belongs to. See the Clients resource.
  • status — one of "draft", "scheduled", "in-progress", or "completed". Most external systems should create jobs with status: "scheduled".
  • priority — string priority label used by the AI Dispatcher and dashboard filters.
  • startDateTime and endDateTime — UTC ISO-8601 timestamps.
  • assignedToTeams — array of user IDs.
  • lineItems — the products and services on the job. See Products and services.
  • subTotal, discount, tax, total — money totals as numbers (in your account's base currency).
  • notes — a plain string with any free-form notes. No longer a separate form field.

You do not have to send subTotal/tax/total if your line items are FieldCamp products with prices and you want the server to compute totals. Send them when your upstream system is the source of truth for pricing.

Example request

curl -X POST https://api.fieldcamp.ai/api/v1/jobs \
  -H "Authorization: Bearer $FIELDCAMP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "one-off",
    "status": "scheduled",
    "priority": "normal",
    "clientId": "cli_01H...",
    "startDateTime": "2026-06-03T14:00:00.000Z",
    "endDateTime": "2026-06-03T16:00:00.000Z",
    "timezone": "America/Los_Angeles",
    "assignedToTeams": ["usr_01H..."],
    "lineItems": [
      { "productId": "prd_01H...", "quantity": 1, "unitPrice": 250 }
    ],
    "subTotal": 250,
    "discount": 0,
    "tax": 22.5,
    "total": 272.5,
    "notes": "Gate code 4412. Please call on arrival."
  }'

List and filter jobs

GET /api/v1/jobs supports the following query parameters:

  • search — free-text match against job number, title, and client name.
  • clientId — return only jobs for a specific client.
  • status — filter by draft, scheduled, in-progress, or completed.
  • since — return jobs updated after this ISO-8601 timestamp (useful for incremental syncs).

Backfill once

Call GET /api/v1/jobs without since and paginate through the result. Store the largest updatedAt you see.

Sync incrementally

On every subsequent run, pass since={last_updated_at} to fetch only what changed.

Filter by status

For a dispatch board, narrow to status=scheduled plus status=in-progress to get active work only.

Update a job

PUT /api/v1/jobs/{id} accepts the same JSON shape as create. Send only the fields you want to change.

Status lifecycle

The four supported status values are draft, scheduled, in-progress, and completed. A typical lifecycle:

  1. draft — created from a request or quote, not yet on the dispatch board.
  2. scheduled — has a time window and at least one assignee.
  3. in-progress — a crew has started the first visit.
  4. completed — all visits are done and the job is ready to invoice.

For the user-facing equivalents, see the in-app overview of job and visit statuses.

Visits on jobs

A job has a time window (startDateTime/endDateTime), and FieldCamp auto-generates one visit to cover it. For multi-stop jobs (e.g. delivery-then-collection, or a multi-day install) leave visits: [] on create and use POST /api/v1/visits afterwards for full control. See Multi-day jobs overview and the Visits resource.

Typical patterns

  • Online booking sync: create with type: "one-off", status: "scheduled", assignedToTeams: [], and let the AI Dispatcher propose an assignee.
  • Recurring contracts: create with type: "recurring" and a recurrence rule; FieldCamp expands child visits on its own.
  • Quote-to-job: create with status: "draft", attach line items, then PUT to status: "scheduled" once the client accepts.
  • Field completion: when a tech marks a visit complete on mobile, the parent job moves to completed once the last visit is closed.

Endpoints

GET/api/v1/jobs

Authorization

AuthorizationBearer <token>

Pass your JWT API key as Authorization: Bearer <token>. Send alongside X-API-Key: <token> for maximum compatibility.

In: header

Response Body

application/json

application/json

curl -X GET "https://api.fieldcamp.ai/api/v1/jobs"
{
  "success": true,
  "data": {
    "jobs": [
      {
        "clientId": "string",
        "jobNumber": "BOOKING-5512-MOVE",
        "isBillingDifferent": false,
        "billToClientId": "string",
        "billToClientDetails": {},
        "jobAddress": {
          "street": "221B Baker Street",
          "houseNumber": "",
          "city": "London",
          "state": "England",
          "country": "United Kingdom",
          "zipCode": "NW1 6XE",
          "formattedAddress": "221B Baker Street, London, England, NW1 6XE, United Kingdom",
          "latitude": 51.5237,
          "longitude": -0.1585,
          "plusCode": ""
        },
        "jobPhone": {
          "countryCode": "+44",
          "number": "7555123456",
          "countryIdentifier": "gb"
        },
        "assignedToTeams": [
          "string"
        ],
        "jobType": "one-off",
        "startDateTime": "2019-08-24T14:15:22Z",
        "endDateTime": "2019-08-24T14:15:22Z",
        "scheduleLater": false,
        "anyTime": false,
        "serviceDuration": 0,
        "jobFormIds": [
          "string"
        ],
        "subTotal": 0.1,
        "tax": 0.1,
        "total": 0.1,
        "discount": 0,
        "discountType": 1,
        "jobStatus": "scheduled",
        "jobItems": [
          {
            "itemId": "string",
            "id": "string",
            "itemName": "Standard 3-person moving crew",
            "description": "",
            "taxIds": [
              "string"
            ],
            "exemptFromTax": false,
            "quantity": 1,
            "price": 299,
            "total": 299,
            "taxAmount": 0,
            "orderIndex": 1,
            "type": "Service",
            "taxes": [
              {}
            ],
            "action": 1
          }
        ],
        "timezone": "Europe/London",
        "properties": [
          {
            "id": "string",
            "name": "Sub Type",
            "type": "Text",
            "value": "Delivery",
            "options": [
              "string"
            ],
            "isRequired": true,
            "source": "fieldDefinition"
          }
        ],
        "isStartTime": true,
        "isEndTime": true,
        "skillsId": [
          "string"
        ],
        "priority": "medium",
        "notesLinking": {
          "linkToEstimate": true,
          "linkToInvoice": true
        },
        "jobLogsTemplateId": "string",
        "templateId": "string",
        "templateSnapshot": {},
        "visits": [
          {
            "visitStartDateTime": "2026-05-01T09:00:00.000Z",
            "visitEndDateTime": "2026-05-01T17:00:00.000Z",
            "teamId": [
              "string"
            ],
            "visitStatus": "scheduled"
          }
        ],
        "id": "string",
        "recordId": "string",
        "createdAt": "2019-08-24T14:15:22Z",
        "updatedAt": "2019-08-24T14:15:22Z"
      }
    ]
  }
}
{
  "success": false,
  "error": "Invalid or expired token"
}
POST/api/v1/jobs

Authorization

AuthorizationBearer <token>

Pass your JWT API key as Authorization: Bearer <token>. Send alongside X-API-Key: <token> for maximum compatibility.

In: header

Request Body

multipart/form-data

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

curl -X POST "https://api.fieldcamp.ai/api/v1/jobs" \  -F jobData="string" \  -F notes="string"
{
  "success": true,
  "data": {
    "jobId": "string",
    "job": {
      "clientId": "string",
      "jobNumber": "BOOKING-5512-MOVE",
      "isBillingDifferent": false,
      "billToClientId": "string",
      "billToClientDetails": {},
      "jobAddress": {
        "street": "221B Baker Street",
        "houseNumber": "",
        "city": "London",
        "state": "England",
        "country": "United Kingdom",
        "zipCode": "NW1 6XE",
        "formattedAddress": "221B Baker Street, London, England, NW1 6XE, United Kingdom",
        "latitude": 51.5237,
        "longitude": -0.1585,
        "plusCode": ""
      },
      "jobPhone": {
        "countryCode": "+44",
        "number": "7555123456",
        "countryIdentifier": "gb"
      },
      "assignedToTeams": [
        "string"
      ],
      "jobType": "one-off",
      "startDateTime": "2019-08-24T14:15:22Z",
      "endDateTime": "2019-08-24T14:15:22Z",
      "scheduleLater": false,
      "anyTime": false,
      "serviceDuration": 0,
      "jobFormIds": [
        "string"
      ],
      "subTotal": 0.1,
      "tax": 0.1,
      "total": 0.1,
      "discount": 0,
      "discountType": 1,
      "jobStatus": "scheduled",
      "jobItems": [
        {
          "itemId": "string",
          "id": "string",
          "itemName": "Standard 3-person moving crew",
          "description": "",
          "taxIds": [
            "string"
          ],
          "exemptFromTax": false,
          "quantity": 1,
          "price": 299,
          "total": 299,
          "taxAmount": 0,
          "orderIndex": 1,
          "type": "Service",
          "taxes": [
            {}
          ],
          "action": 1
        }
      ],
      "timezone": "Europe/London",
      "properties": [
        {
          "id": "string",
          "name": "Sub Type",
          "type": "Text",
          "value": "Delivery",
          "options": [
            "string"
          ],
          "isRequired": true,
          "source": "fieldDefinition"
        }
      ],
      "isStartTime": true,
      "isEndTime": true,
      "skillsId": [
        "string"
      ],
      "priority": "medium",
      "notesLinking": {
        "linkToEstimate": true,
        "linkToInvoice": true
      },
      "jobLogsTemplateId": "string",
      "templateId": "string",
      "templateSnapshot": {},
      "visits": [
        {
          "visitStartDateTime": "2026-05-01T09:00:00.000Z",
          "visitEndDateTime": "2026-05-01T17:00:00.000Z",
          "teamId": [
            "string"
          ],
          "visitStatus": "scheduled"
        }
      ],
      "id": "string",
      "recordId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  }
}
{
  "success": false,
  "error": "jobNumber is required"
}
{
  "success": false,
  "error": "Invalid or expired token"
}
GET/api/v1/jobs/{id}

Authorization

AuthorizationBearer <token>

Pass your JWT API key as Authorization: Bearer <token>. Send alongside X-API-Key: <token> for maximum compatibility.

In: header

Path Parameters

id*string

Response Body

application/json

application/json

curl -X GET "https://api.fieldcamp.ai/api/v1/jobs/string"
{
  "success": true,
  "data": {
    "job": {
      "clientId": "string",
      "jobNumber": "BOOKING-5512-MOVE",
      "isBillingDifferent": false,
      "billToClientId": "string",
      "billToClientDetails": {},
      "jobAddress": {
        "street": "221B Baker Street",
        "houseNumber": "",
        "city": "London",
        "state": "England",
        "country": "United Kingdom",
        "zipCode": "NW1 6XE",
        "formattedAddress": "221B Baker Street, London, England, NW1 6XE, United Kingdom",
        "latitude": 51.5237,
        "longitude": -0.1585,
        "plusCode": ""
      },
      "jobPhone": {
        "countryCode": "+44",
        "number": "7555123456",
        "countryIdentifier": "gb"
      },
      "assignedToTeams": [
        "string"
      ],
      "jobType": "one-off",
      "startDateTime": "2019-08-24T14:15:22Z",
      "endDateTime": "2019-08-24T14:15:22Z",
      "scheduleLater": false,
      "anyTime": false,
      "serviceDuration": 0,
      "jobFormIds": [
        "string"
      ],
      "subTotal": 0.1,
      "tax": 0.1,
      "total": 0.1,
      "discount": 0,
      "discountType": 1,
      "jobStatus": "scheduled",
      "jobItems": [
        {
          "itemId": "string",
          "id": "string",
          "itemName": "Standard 3-person moving crew",
          "description": "",
          "taxIds": [
            "string"
          ],
          "exemptFromTax": false,
          "quantity": 1,
          "price": 299,
          "total": 299,
          "taxAmount": 0,
          "orderIndex": 1,
          "type": "Service",
          "taxes": [
            {}
          ],
          "action": 1
        }
      ],
      "timezone": "Europe/London",
      "properties": [
        {
          "id": "string",
          "name": "Sub Type",
          "type": "Text",
          "value": "Delivery",
          "options": [
            "string"
          ],
          "isRequired": true,
          "source": "fieldDefinition"
        }
      ],
      "isStartTime": true,
      "isEndTime": true,
      "skillsId": [
        "string"
      ],
      "priority": "medium",
      "notesLinking": {
        "linkToEstimate": true,
        "linkToInvoice": true
      },
      "jobLogsTemplateId": "string",
      "templateId": "string",
      "templateSnapshot": {},
      "visits": [
        {
          "visitStartDateTime": "2026-05-01T09:00:00.000Z",
          "visitEndDateTime": "2026-05-01T17:00:00.000Z",
          "teamId": [
            "string"
          ],
          "visitStatus": "scheduled"
        }
      ],
      "id": "string",
      "recordId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  }
}
{
  "success": false,
  "error": "Invalid or expired token"
}
Empty
PUT/api/v1/jobs/{id}

Authorization

AuthorizationBearer <token>

Pass your JWT API key as Authorization: Bearer <token>. Send alongside X-API-Key: <token> for maximum compatibility.

In: header

Path Parameters

id*string

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Any subset of NewJobData fields.

Response Body

application/json

application/json

curl -X PUT "https://api.fieldcamp.ai/api/v1/jobs/string" \  -H "Content-Type: application/json" \  -d '{    "jobStatus": "cancelled"  }'
{
  "success": true,
  "data": {
    "job": {
      "clientId": "string",
      "jobNumber": "BOOKING-5512-MOVE",
      "isBillingDifferent": false,
      "billToClientId": "string",
      "billToClientDetails": {},
      "jobAddress": {
        "street": "221B Baker Street",
        "houseNumber": "",
        "city": "London",
        "state": "England",
        "country": "United Kingdom",
        "zipCode": "NW1 6XE",
        "formattedAddress": "221B Baker Street, London, England, NW1 6XE, United Kingdom",
        "latitude": 51.5237,
        "longitude": -0.1585,
        "plusCode": ""
      },
      "jobPhone": {
        "countryCode": "+44",
        "number": "7555123456",
        "countryIdentifier": "gb"
      },
      "assignedToTeams": [
        "string"
      ],
      "jobType": "one-off",
      "startDateTime": "2019-08-24T14:15:22Z",
      "endDateTime": "2019-08-24T14:15:22Z",
      "scheduleLater": false,
      "anyTime": false,
      "serviceDuration": 0,
      "jobFormIds": [
        "string"
      ],
      "subTotal": 0.1,
      "tax": 0.1,
      "total": 0.1,
      "discount": 0,
      "discountType": 1,
      "jobStatus": "scheduled",
      "jobItems": [
        {
          "itemId": "string",
          "id": "string",
          "itemName": "Standard 3-person moving crew",
          "description": "",
          "taxIds": [
            "string"
          ],
          "exemptFromTax": false,
          "quantity": 1,
          "price": 299,
          "total": 299,
          "taxAmount": 0,
          "orderIndex": 1,
          "type": "Service",
          "taxes": [
            {}
          ],
          "action": 1
        }
      ],
      "timezone": "Europe/London",
      "properties": [
        {
          "id": "string",
          "name": "Sub Type",
          "type": "Text",
          "value": "Delivery",
          "options": [
            "string"
          ],
          "isRequired": true,
          "source": "fieldDefinition"
        }
      ],
      "isStartTime": true,
      "isEndTime": true,
      "skillsId": [
        "string"
      ],
      "priority": "medium",
      "notesLinking": {
        "linkToEstimate": true,
        "linkToInvoice": true
      },
      "jobLogsTemplateId": "string",
      "templateId": "string",
      "templateSnapshot": {},
      "visits": [
        {
          "visitStartDateTime": "2026-05-01T09:00:00.000Z",
          "visitEndDateTime": "2026-05-01T17:00:00.000Z",
          "teamId": [
            "string"
          ],
          "visitStatus": "scheduled"
        }
      ],
      "id": "string",
      "recordId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  }
}
{
  "success": false,
  "error": "Invalid or expired token"
}
Empty
DELETE/api/v1/jobs/{id}

Authorization

AuthorizationBearer <token>

Pass your JWT API key as Authorization: Bearer <token>. Send alongside X-API-Key: <token> for maximum compatibility.

In: header

Path Parameters

id*string

Response Body

application/json

curl -X DELETE "https://api.fieldcamp.ai/api/v1/jobs/string"
Empty
{
  "success": false,
  "error": "Invalid or expired token"
}
Empty
GET/api/v1/jobs/{id}/items

Authorization

AuthorizationBearer <token>

Pass your JWT API key as Authorization: Bearer <token>. Send alongside X-API-Key: <token> for maximum compatibility.

In: header

Path Parameters

id*string

Response Body

application/json

application/json

curl -X GET "https://api.fieldcamp.ai/api/v1/jobs/string/items"
{
  "success": true,
  "data": {
    "items": [
      {
        "itemId": "string",
        "id": "string",
        "itemName": "Standard 3-person moving crew",
        "description": "",
        "taxIds": [
          "string"
        ],
        "exemptFromTax": false,
        "quantity": 1,
        "price": 299,
        "total": 299,
        "taxAmount": 0,
        "orderIndex": 1,
        "type": "Service",
        "taxes": [
          {}
        ],
        "action": 1
      }
    ]
  }
}
{
  "success": false,
  "error": "Invalid or expired token"
}
Empty

Troubleshooting

400 jobNumber is required

You are sending multipart/form-data to the v1 endpoint. Switch to Content-Type: application/json and send the job object directly — no jobData wrapper.

422 invalid status

Make sure status is one of draft, scheduled, in-progress, or completed. Other strings (e.g. open, pending) are rejected.

Assignees are not showing on the calendar

Check that assignedToTeams contains user IDs from GET /api/v1/team, not team or role IDs.

Totals are wrong in the UI

Either send subTotal/discount/tax/total together, or omit all of them and let line-item pricing compute server-side. Mixing the two leads to mismatched values.

FAQs

Can I still call /api/jobs (no v1)? The unversioned route is deprecated. New integrations should target /api/v1/jobs. Existing callers should migrate before the next major release.

What is the difference between type: "one-off" and type: "recurring"? One-off jobs have a single time window. Recurring jobs carry a recurrence rule and FieldCamp expands them into a series of visits — see Multi-day jobs overview.

How do I do an incremental sync? Use GET /api/v1/jobs?since={iso_timestamp} on a schedule. Store the latest updatedAt you observe so each run only pulls deltas.

How do I filter by client? Pass clientId= on GET /api/v1/jobs. Combine with status and since to narrow the result further.

Where do I get user IDs for assignedToTeams? GET /api/v1/team returns the active users. See the Team resource for the full payload shape.

On this page