Clients | FieldCamp API
Use the FieldCamp Clients API to create, search, update, soft-delete, and sync the people and companies you do work for.
The FieldCamp Clients API is how your integration creates and maintains the people or companies you do work for. Every job, estimate, invoice, and visit eventually references a client, so most integrations create a client once on first contact and then reuse the same client ID on every follow-up. With release_v2, the resource is available under the versioned path /api/v1/clients, which adds server-side search, stage, and since filters, a soft-delete endpoint, and geocoded address fields.
What changed in release_v2
The /api/v1/clients namespace is now the recommended path for new integrations. The legacy /api/clients endpoints still exist for backward compatibility, but they do not expose the new filters or the geocoded address fields, and we strongly recommend migrating.
- Versioned base path:
/api/v1/clients(instead of/api/clients). - Server-side filters on the list endpoint:
search,stage, andsince. - New
DELETE /api/v1/clients/{id}soft-delete endpoint that hides the client from the UI without losing history. - Address coordinates:
latitude,longitudeandplusCodeare sub-fields inside eachAddressobject, and you supply them — there is no server-side geocoding on this endpoint. - Custom properties on both create and update, via
properties. - Consistent stage vocabulary aligned with client categories and stages used in the FieldCamp UI.
If you previously built your own deduplication loop on top of GET /api/clients, you can now collapse it into a single GET /api/v1/clients?search=jane@acme.com call. See Idempotency for safe-retry patterns.
Fields worth knowing
emailis still the de-facto lookup key. Always store it. Withrelease_v2you can pass it to thesearchquery parameter instead of paginating client-side.phoneNumberis structured —{ countryCode, number, countryIdentifier }. See the PhoneNumber block in theNewClientschema rendered below.billingAddressandpropertyAddressare independent, and both are optional. Pass the sameAddressobject to both if they are identical. The only required-field rule on create is that you send eitherfirstNameoremail— everything else, including both addresses, can be omitted.propertiescarries your account's custom client fields. Send an array of{ id, value }(extra keys likenameortypeare tolerated and ignored) or an object keyed by field id. Discover the ids withGET /api/v1/objects/client/schema— see Custom Objects. OnPUTthe merge is partial by field id, so properties you do not mention are preserved; sendvalue: nullto clear one.stageis free-text but commonly one ofLead,Active Client, orInactive. Thestagequery parameter on the list endpoint matches the same vocabulary you configure in the sales pipeline.latitude/longitude/plusCodeare optional sub-fields of anAddressobject — not top-level client fields, and not filled in for you. Supply them when you have them: they are the coordinates the dispatcher uses for route optimization and AI dispatching, so an address without them still geocodes at dispatch time rather than at create time.
Authentication
All requests to /api/v1/clients require a valid fc_live key passed via the X-Api-Key header with appropriate scopes — see Authentication for setup, and API key management to create and rotate keys per integration.
Filtering and search
GET /api/v1/clients accepts the following query parameters:
search— free-text match against client name, email, and phone number. Case-insensitive, partial-match.stage— exact match against the client's stage field (for exampleLead,Active Client,Inactive).since— ISO 8601 timestamp. Returns clients whoseupdatedAtis at or after the supplied value. Send it with a timezone (…Zor an offset), or add thetimezoneparam for a zone-less value (otherwise it's read as UTC). For a reliable delta sync, pair it withsort=updatedAt:ascand use the last row'supdatedAtas your nextsince.sort— order asfield:direction(defaultdesc), e.g.sort=updatedAt:asc. Sortable fields:createdAt,updatedAt,firstName,lastName,companyName,stage.timezone— IANA timezone name (e.g.America/Denver) used to interpret a zone-lesssince.pageandlimit— offset pagination.pageis 1-based (default1);limitdefaults to30and is capped at100. A larger value likelimit=1000is not rejected — it is silently clamped to 100, so you still get200with only 100 rows. Readmeta.totalfor the real count and page through the rest withpage.
Use since for nightly syncs into your data warehouse. Keep the highest updatedAt you have seen and pass it back on the next run — you only pull the records that actually changed. See Incremental sync for the full recipe. The same pattern powers the calendar and accounting sync API.
Example: find by email
curl https://api.fieldcamp.ai/api/v1/clients?search=jane@acme.com \
-H "Authorization: Bearer $FIELDCAMP_API_KEY"Example: pull leads only
curl "https://api.fieldcamp.ai/api/v1/clients?stage=Lead&limit=100" \
-H "Authorization: Bearer $FIELDCAMP_API_KEY"Example: incremental sync
curl "https://api.fieldcamp.ai/api/v1/clients?since=2026-05-01T00:00:00Z" \
-H "Authorization: Bearer $FIELDCAMP_API_KEY"If you exceed the per-minute request budget, you will receive an HTTP 429 with throttling headers — see Rate limits for the sliding-window details and back-off guidance.
Soft delete
DELETE /api/v1/clients/{id} performs a soft delete. The client is hidden from the FieldCamp UI and excluded from default list responses, but their historical jobs, invoices, and payments remain intact. This mirrors the in-app delete or archive a client flow.
Soft-deleted clients are excluded from GET /api/v1/clients by default. If you maintain a mirror of the table in your own system, listen for the client.deleted webhook so you can mark the row inactive on your side too. See Webhooks and the webhook events catalog for payload shapes.
There is no restore over the API. PUT /api/v1/clients/{id} returns 404 NOT_FOUND once a client is soft-deleted, and isDeleted, deletedAt and status are outside the update contract — sending any of them returns 400. Restore the client from the app, or ask us if you need it done in bulk.
Creating clients safely
Search first
Call GET /api/v1/clients?search=<email> before creating. If you get a hit, reuse the existing client ID.
De-duplicate on your side
No idempotency header is read on this endpoint today, and email has no unique index — a retried POST creates a second client. So the search above is your de-duplication, and it is worth storing the FieldCamp id against your own record so a retry updates rather than re-creates. See Idempotency for the patterns we recommend until server-side keys ship.
Send the addresses you have
Both billingAddress and propertyAddress are optional and independent — post the same Address object to both when they are identical. Include latitude / longitude inside the address if you already hold them.
Send your custom fields too
Read the field ids once from GET /api/v1/objects/client/schema and pass them as properties. A create that skips them lands a client your team then has to complete by hand.
If you are wiring this in for the first time, the API quickstart walks through the full flow from issuing an fc_live key to scheduling a job.
Endpoints
/api/v1/clientsAuthorization
BearerAuth Your FieldCamp API key (starts with fc_live_). Send it as Authorization: Bearer fc_live_… — or as the X-Api-Key: fc_live_… header. Create a key in Settings → API. This is an API key, not a login JWT.
In: header
Query Parameters
Page number, 1-based.
Rows per page. Values above 100 are clamped to 100.
Return only rows whose updatedAt is at or after this ISO-8601 timestamp — the incremental-sync cursor. A value that is not a date returns 400 rather than being ignored. Include a timezone (…Z or an offset), or pass the timezone param for a zone-less value; otherwise it is read as UTC.
IANA timezone name (e.g. America/Denver) used to interpret a since that has no timezone of its own. Ignored when since already carries a Z or an offset.
Order the result as field:direction. Direction is asc or desc (default desc). Sortable fields: createdAt, updatedAt, firstName, lastName, companyName, stage. An unsupported field returns 400.
Free-text match on name, email, phone and company name.
Filter by client stage, matched case-insensitively. Valid values are your account's pipeline stage keys — read them from GET /api/v1/objects/client/schema.
Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/api/v1/clients"{}/api/v1/clientsAuthorization
BearerAuth Your FieldCamp API key (starts with fc_live_). Send it as Authorization: Bearer fc_live_… — or as the X-Api-Key: fc_live_… header. Create a key in Settings → API. This is an API key, not a login JWT.
In: header
Request Body
application/json
Any key outside this list returns 400 with the offending key named — this API never accepts and silently discards a field.
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
curl -X POST "https://example.com/api/v1/clients" \ -H "Content-Type: application/json" \ -d '{}'{}/api/v1/clients/{id}Authorization
BearerAuth Your FieldCamp API key (starts with fc_live_). Send it as Authorization: Bearer fc_live_… — or as the X-Api-Key: fc_live_… header. Create a key in Settings → API. This is an API key, not a login JWT.
In: header
Path Parameters
Response Body
application/json
application/json
curl -X GET "https://example.com/api/v1/clients/665f1c2ab3e4d5f6a7b8c9d0"{}/api/v1/clients/{id}Authorization
BearerAuth Your FieldCamp API key (starts with fc_live_). Send it as Authorization: Bearer fc_live_… — or as the X-Api-Key: fc_live_… header. Create a key in Settings → API. This is an API key, not a login JWT.
In: header
Path Parameters
Request Body
application/json
Any key outside this list returns 400 with the offending key named — this API never accepts and silently discards a field.
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
curl -X PUT "https://example.com/api/v1/clients/665f1c2ab3e4d5f6a7b8c9d0" \ -H "Content-Type: application/json" \ -d '{}'{}/api/v1/clients/{id}Authorization
BearerAuth Your FieldCamp API key (starts with fc_live_). Send it as Authorization: Bearer fc_live_… — or as the X-Api-Key: fc_live_… header. Create a key in Settings → API. This is an API key, not a login JWT.
In: header
Path Parameters
Response Body
application/json
application/json
curl -X DELETE "https://example.com/api/v1/clients/665f1c2ab3e4d5f6a7b8c9d0"{}Troubleshooting
search returns no rows but the client clearly exists.
The search filter excludes soft-deleted clients by default. Try the same call without search and look for the record; if you find it via direct GET /api/v1/clients/{id}, it has likely been archived through the client detail page actions menu.
Coordinates are missing on a freshly created client.
They are not filled in for you — latitude, longitude and plusCode live inside the Address object you send, so a client created without them simply has none. Include them in the address on create, or update the client later with a complete address.
stage filter returns nothing.
The match is exact and case-sensitive on the canonical value. Confirm the stage string you are passing matches the value configured in your pipeline — see client categories and stages for the defaults.
I am getting 4xx responses I do not expect. Check the response envelope and HTTP status against the errors and retries reference before adding new logic — most transient failures have a documented retry pattern.
I am still hitting the old /api/clients paths.
The legacy paths are kept for backward compatibility and will continue to work, but they do not return geocoded fields and do not accept search, stage, or since. Migrate to /api/v1/clients to unlock those features and to keep up with future schema additions documented in the changelog.
FAQs
Can I hard-delete a client?
No. DELETE /api/v1/clients/{id} is always a soft delete. This is intentional — clients are referenced by historical jobs and invoices, and hard deletion would break those records.
Is there a bulk import endpoint?
Not yet. For initial migrations, use the in-app CSV import for very large lists, or loop POST /api/v1/clients with an Idempotency-Key per row for smaller batches.
How do I model leads versus active customers?
Set the stage field. The same vocabulary powers the sales pipeline kanban board so your CRM stays consistent across API and UI.
Why is the email field still optional?
Some field-service customers (especially walk-in or referral work) only have a phone number. Email remains optional, but if you have it, send it — search uses it as the primary match key.
How do I keep my CRM in sync without polling every minute?
Combine the since filter for delta pulls with webhooks for real-time client.created, client.updated, and client.deleted events.
Related articles
- API quickstart
- Authentication
- Idempotency
- Rate limits
- Errors and retries
- Webhooks
- Webhook events catalog
- API changelog
- Jobs API
- Invoices API
- Client categories and stages
- Delete or archive a client
- Client detail page actions menu
- Import clients into FieldCamp
- Route optimization
- How AI Dispatcher works
- Sales pipeline kanban board
FieldCamp Webhook Events Catalog: Event Types and Payloads
Reference for every FieldCamp webhook event, including sample payloads, required scopes, and tips for building reliable webhook receivers.
Custom Objects | FieldCamp API
Read and write records on the custom objects your FieldCamp account defines — runtime schema discovery, cursor paging, delta sync, upsert by your own id, and object.* webhooks.