FieldCamp
Resources

Visits | FieldCamp API

The FieldCamp Visits API schedules, updates, cancels, and tracks on-site appearances for jobs across an 8-state visit lifecycle.

The FieldCamp Visits API represents scheduled on-site appearances for a job. A single job may have one visit (a quick service call) or several visits chained together (a delivery followed by a collection, a diagnostic followed by a repair, or a multi-day install). The v1 endpoints expose the full visit lifecycle — from creation through dispatch, arrival, completion, and cancellation — and integrate with FieldCamp's AI Dispatcher and live team tracking.

When to create visits explicitly

For most simple jobs, FieldCamp auto-generates a visit from the job's time window when you POST /api/v1/jobs with visits: []. Create visits explicitly via POST /api/v1/visits when you need:

  • More than one visit per job — e.g. a two-stop install, or a return trip for a part that was on backorder.
  • Explicit control over scheduledStart / scheduledEnd — instead of inheriting the job's window.
  • A different team or technician than the parent job — e.g. the estimator visits first, then the install crew.
  • Unscheduled or anytime visits — placeholders that sit on the unscheduled queue until your dispatcher assigns a slot.
  • Different serviceDuration — useful when one stop is long (an install) and another is short (a follow-up walkthrough).

If you let FieldCamp auto-generate the visit, the AI Dispatcher can still optimize the assignment using skills, capacity, and travel time. You don't lose intelligence by skipping explicit visit creation.

Visit lifecycle and visitStatus

Every visit moves through a defined lifecycle. The visitStatus field is the single source of truth and powers the dispatch calendar, live tracking, and reporting. You can filter GET /api/v1/visits?visitStatus=... by any of the eight states below — and pass multiple values to fetch several at once.

visitStatusMeaning
scheduledAssigned to a team with a fixed start and end time.
unscheduledCreated without a time slot — sits on the unscheduled queue.
in_transitTechnician is driving to the site (GPS confirmed).
arrivedTechnician has reached the location but hasn't started work.
in_progressWork is actively being performed.
pausedWork was started then paused (lunch, awaiting a part, customer not ready).
completedThe visit has been finished and submitted from the field.
cancelledThe visit was cancelled before completion.

Status transitions are driven by the mobile app, dispatch actions, and webhook automations. You can write any allowed status via PUT /api/v1/visits/:id, but the dispatcher will reject transitions that don't make sense (e.g. moving from completed back to scheduled requires re-opening the visit).

See Job and visit statuses for the parent-job mapping of these states.

Fields

  • jobId — the parent job, returned from POST /api/v1/jobs.
  • scheduledStart / scheduledEnd — UTC ISO-8601 datetimes. Omit both with unscheduled: true.
  • teamId[] — an array of user IDs from GET /api/v1/team. The Visits API accepts multiple assignees per visit, so a two-person crew is a single visit, not two. Replaces the old single technicianId field.
  • serviceDuration — the planned on-site duration in minutes. Used by the dispatcher to fit the visit into the team's capacity even when scheduledEnd is not yet pinned.
  • unscheduled — boolean. When true, the visit is created without a time slot and sits on the dispatch queue.
  • anyTime — boolean. Marks the visit as flexible-within-day (no fixed hour). The dispatcher will slot it into the first open window.
  • visitStatus — one of the eight values in the lifecycle table above.

teamId is always an array, even for a one-person visit (teamId: ["usr_..."]). Sending a bare string from the old technicianId field will return a 400 Bad Request.

Creating a visit

Create or look up the parent job

Every visit belongs to a job. Either POST /api/v1/jobs first or look up the jobId from your existing record. See the Jobs resource for the full job payload.

Choose a team

Fetch your active team via GET /api/v1/team and assemble a teamId[] array. For one-person jobs that's a single-element array; for two-person crews list both user IDs.

Decide on scheduling mode

Pick one: a fixed scheduledStart/scheduledEnd, anyTime: true with a date, or unscheduled: true. Mixing fixed times with unscheduled: true is not allowed.

POST the visit

Send POST /api/v1/visits with jobId, teamId[], scheduling fields, and an optional serviceDuration. The response includes the new visitId and a visitStatus of either scheduled or unscheduled.

Confirm on the dispatch calendar

New visits surface immediately on the dispatch calendar. Live tracking turns on automatically once a team member starts the visit on mobile.

Listing and filtering visits

GET /api/v1/visits supports filtering by visitStatus, teamId, jobId, and date range. Common patterns:

  • Today's active work?visitStatus=in_transit,arrived,in_progress,paused.
  • Backlog for a dispatcher?visitStatus=unscheduled.
  • A crew's day?teamId=usr_abc&from=2026-05-29T00:00:00Z&to=2026-05-29T23:59:59Z.
  • What got cancelled last week?visitStatus=cancelled&from=...&to=... for churn analysis.

Pair these queries with FieldCamp's route optimization and AI Dispatcher to keep the day moving when statuses change in real time.

Updating and cancelling visits

PUT /api/v1/visits/:id lets you reschedule, reassign, change duration, or push a status forward. Common updates include:

  • Reassigning a visit to a different teamId[] after a call-out.
  • Bumping scheduledStart/scheduledEnd when a prior visit overruns.
  • Setting visitStatus: "cancelled" with a reason note when the customer cancels.

DELETE /api/v1/visits/:id permanently removes a visit. Use this for clearing test data or removing a duplicate that was created in error — it does not preserve audit history. For a customer-facing cancellation, prefer PUT with visitStatus: "cancelled" so the timeline keeps the record.

Endpoints

GET/api/v1/visits

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/visits"
{
  "success": true,
  "data": {
    "visits": [
      {
        "jobId": "string",
        "scheduledStart": "2019-08-24T14:15:22Z",
        "scheduledEnd": "2019-08-24T14:15:22Z",
        "technicianId": "string",
        "id": "string",
        "visitStatus": "scheduled",
        "createdAt": "2019-08-24T14:15:22Z"
      }
    ]
  }
}
{
  "success": false,
  "error": "Invalid or expired token"
}
POST/api/v1/visits

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

application/json

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/visits" \  -H "Content-Type: application/json" \  -d '{    "jobId": "job_abc123",    "scheduledStart": "2026-05-01T09:00:00.000Z",    "scheduledEnd": "2026-05-01T17:00:00.000Z",    "technicianId": "user_xyz789"  }'
{
  "success": true,
  "data": {
    "visitId": "string",
    "visit": {
      "jobId": "string",
      "scheduledStart": "2019-08-24T14:15:22Z",
      "scheduledEnd": "2019-08-24T14:15:22Z",
      "technicianId": "string",
      "id": "string",
      "visitStatus": "scheduled",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  }
}
{
  "success": false,
  "error": "jobNumber is required"
}
{
  "success": false,
  "error": "Invalid or expired token"
}
PUT/api/v1/visits/{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 NewVisit fields.

Response Body

application/json

application/json

curl -X PUT "https://api.fieldcamp.ai/api/v1/visits/string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "success": true,
  "data": {
    "visit": {
      "jobId": "string",
      "scheduledStart": "2019-08-24T14:15:22Z",
      "scheduledEnd": "2019-08-24T14:15:22Z",
      "technicianId": "string",
      "id": "string",
      "visitStatus": "scheduled",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  }
}
{
  "success": false,
  "error": "Invalid or expired token"
}
Empty
DELETE/api/v1/visits/{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/visits/string"
Empty
{
  "success": false,
  "error": "Invalid or expired token"
}
Empty

Best practices

Troubleshooting

Why am I getting 400: technicianId is not allowed? The legacy technicianId field was replaced by teamId[]. Convert single IDs to a one-element array.

My visit was created but doesn't appear on the calendar. Check visitStatus. If it's unscheduled or anyTime: true, it lives on the unscheduled queue until a slot is set. The dispatch calendar has a separate panel for these.

Can I move a completed visit back to in_progress? Only by re-opening it from the FieldCamp UI. The API will reject the direct status transition to protect reporting integrity.

How do I assign a two-person crew? Pass both user IDs in teamId[] on the same visit. Do not create two visits — that double-counts the work in capacity planning.

Does deleting a visit delete the job? No. Visits and jobs are separate resources. Deleting all visits on a job leaves the job in place; FieldCamp will prompt a dispatcher to add a new visit or close the job.

FAQs

What's the difference between scheduled and arrived? scheduled means a time slot exists. arrived means the technician is physically on-site (confirmed via GPS or a manual mobile action) but hasn't started work yet.

Can the AI Dispatcher create visits on my behalf? Yes. When you submit a job for AI dispatch, it creates the visit, assigns the team, and sets the status — all via the same v1 endpoints.

Are unscheduled visits counted against capacity? Only after they are assigned and a scheduledStart is set. Until then they live in the backlog and don't reserve a slot.

On this page