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 withstatus: "scheduled".priority— string priority label used by the AI Dispatcher and dashboard filters.startDateTimeandendDateTime— 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 bydraft,scheduled,in-progress, orcompleted.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.
PUT /api/v1/jobs/{id} with { startDateTime, endDateTime }.
Set status: "cancelled" via PUT. Prefer this over DELETE for auditability.
Set status: "completed" when the crew finishes; visits roll up automatically.
Use GET /api/v1/jobs?search=<jobNumber> before re-creating.
Status lifecycle
The four supported status values are draft, scheduled, in-progress, and completed. A typical lifecycle:
- draft — created from a request or quote, not yet on the dispatch board.
- scheduled — has a time window and at least one assignee.
- in-progress — a crew has started the first visit.
- 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, thenPUTtostatus: "scheduled"once the client accepts. - Field completion: when a tech marks a visit complete on mobile, the parent job moves to
completedonce the last visit is closed.
Endpoints
/api/v1/jobsPass 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"
}/api/v1/jobsPass 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"
}/api/v1/jobs/{id}Pass your JWT API key as Authorization: Bearer <token>. Send
alongside X-API-Key: <token> for maximum compatibility.
In: header
Path Parameters
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"
}/api/v1/jobs/{id}Pass your JWT API key as Authorization: Bearer <token>. Send
alongside X-API-Key: <token> for maximum compatibility.
In: header
Path Parameters
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"
}/api/v1/jobs/{id}Pass your JWT API key as Authorization: Bearer <token>. Send
alongside X-API-Key: <token> for maximum compatibility.
In: header
Path Parameters
Response Body
application/json
curl -X DELETE "https://api.fieldcamp.ai/api/v1/jobs/string"{
"success": false,
"error": "Invalid or expired token"
}/api/v1/jobs/{id}/itemsPass your JWT API key as Authorization: Bearer <token>. Send
alongside X-API-Key: <token> for maximum compatibility.
In: header
Path Parameters
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"
}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.