JobField.

JobField API.

Last updated 2026-10-17

The JobField API is what the JobField integration on Zapier uses to read and add records in a business's JobField account, and to hear about events as they happen. This page describes every endpoint the integration calls, how a connection is made, and what each request and event looks like.

Contents

  1. Overview
  2. Connecting a business
  3. Making requests
  4. Errors
  5. Rate limits
  6. Lists and pagination
  7. Repeated requests
  8. Instant triggers
  9. Endpoints
  10. Records
  11. Changes to this page

1. Overview

What Value
Base URL https://my.jobfield.app/api/v1
Format JSON in and out, UTF-8
Sign-in OAuth 2.0 authorization code grant, with PKCE
Access token Sent in the Authorization header on every request
Dates ISO 8601 in UTC, for example 2026-09-24T14:00:00.000Z
Money Dollars as a number. Totals and amounts also come in cents beside them.
Support support@jobfield.app

The API serves apps that JobField has registered. Today that is the JobField integration on Zapier. A business connects it by signing in to JobField and pressing Allow. To ask about access for another app, write to support@jobfield.app.

Each connection belongs to one business. Every request reads and writes only that business's records, and the business always comes from the access token. No request can name a different business.

A connection works while three things hold. The business's plan includes Zapier, the JobField account is not locked, and the person who pressed Allow is still an owner or an admin of the business.

What the Zapier integration calls

Zapier step Endpoint
Connection test and label GET /me
New Booking Request Instant trigger booking_request.created
New Customer Instant trigger customer.created
New Job Instant trigger job.created
Job Status Changed Instant trigger job.status_changed
Job Completed Instant trigger job.completed
Invoice Sent Instant trigger invoice.sent
Payment Received Instant trigger payment.received
New Call Instant trigger call.completed
Turning a Zap on and off POST /hooks and DELETE /hooks/{id}
Testing a trigger GET /hooks/sample
Create Booking Request POST /booking-requests
Create Customer POST /customers
Create Job POST /jobs
Find Customer POST /customers/search
Find or Create Customer POST /customers/search, then POST /customers when nothing is found
Find Job GET /jobs?number=
Service, technician, arrival window and customer dropdowns GET /service-types, GET /technicians, GET /booking-windows, GET /customers

Two more endpoints are part of the API and are not called by the integration today: GET /booking-requests and GET /jobs with a status filter. They are described below as well.

2. Connecting a business

JobField runs its own OAuth 2.0 server. The integration uses the authorization code grant with a PKCE S256 challenge.

Endpoint Address
Allow screen GET https://my.jobfield.app/oauth/authorize
Token POST https://my.jobfield.app/api/oauth/token
Server metadata GET https://my.jobfield.app/.well-known/oauth-authorization-server

Lifetimes

Credential Starts with Lasts
Authorization code jfc_ 10 minutes, and works once
Access token jfat_ 1 hour
Refresh token jfrt_ Until the connection ends. It does not change when it is used.

The integration needs no scopes. A connection can do what this page describes and nothing more. The server metadata lists scopes for other kinds of JobField connections, and they do not apply here.

Step 1. Send the person to the Allow screen

Open the Allow screen in the person's browser with these query parameters.

Parameter Required Value
response_type Yes code
client_id Yes The app's ID, given by JobField when the app was registered.
redirect_uri Yes One of the return addresses registered for the app, matched exactly.
state Yes Any value up to 1,000 characters. It comes back unchanged.
code_challenge Recommended The PKCE S256 challenge, 43 to 128 characters of base64url.
code_challenge_method No S256, the only method accepted. When it is left out with a challenge, S256 is assumed.
resource No Leave it out. A value here is refused with invalid_target.

The person signs in to JobField if they are not signed in already. The screen asks them to connect the app to their business and lists what the app can do. They then press Allow or Cancel.

The connection is made for the business JobField opens for that person, which is the first business they joined. Only an owner or an admin can press Allow, and only while the business's plan includes Zapier and the account is not locked. Anyone else sees a sentence that says why the connection cannot be made.

After Allow, the browser returns to redirect_uri with these parameters.

Parameter Value
code The authorization code.
state The value you sent.
iss https://my.jobfield.app

After Cancel, or when the request cannot go on, the browser returns to redirect_uri with error, state and iss. The error values are access_denied, invalid_request, invalid_target and unsupported_response_type. An unknown client_id, or a redirect_uri that is not registered for the app, is never sent back. The screen shows the error instead.

Step 2. Trade the code for tokens

Send a POST to the token endpoint. The body may be form-encoded or JSON. The app proves itself with its secret, either in the body or in an HTTP Basic Authorization header.

Parameter Value
grant_type authorization_code
code The code from step 1.
redirect_uri The same address as in step 1.
client_id The app's ID.
client_secret The app's secret.
code_verifier The PKCE verifier, required when step 1 sent a challenge.
POST /api/oauth/token HTTP/1.1
Host: my.jobfield.app
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=jfc_...&redirect_uri=https%3A%2F%2Fzapier.com%2Fdashboard%2Fauth%2Foauth%2Freturn%2FApp246615CLIAPI%2F&client_id=zapier&client_secret=jfcs_...&code_verifier=...
{
  "access_token": "jfat_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "jfrt_..."
}

A code works once. If a code that was already used is presented again with the app's valid secret, JobField treats it as copied and ends the connection that was made with it. This holds for about a day after the code expires. After that the code is simply not valid.

Step 3. Refresh the access token

When an access token expires, send a POST to the same endpoint.

Parameter Value
grant_type refresh_token
refresh_token The refresh token from step 2.
client_id The app's ID.
client_secret The app's secret.
{
  "access_token": "jfat_...",
  "token_type": "Bearer",
  "expires_in": 3600
}

No new refresh token comes back. Keep the one you have and use it again next time. Refreshing twice at once is safe, and an access token issued earlier keeps working until its hour is over.

Token endpoint errors

Refusals use the OAuth format, {"error": "...", "error_description": "..."}, with the Cache-Control: no-store header.

Status error When
400 invalid_grant The code is not valid, was already used, has expired, or was issued for a different return address. The verifier does not match. The connection was removed. The person who pressed Allow is no longer an owner or an admin. The connection could not be saved, and the person needs to connect again.
400 invalid_target A resource parameter was sent.
400 unsupported_grant_type The grant type is not authorization_code or refresh_token.
401 invalid_client The app's ID or secret is wrong.
429 rate_limited Too many requests from one address. This answer uses the API error format in section 4 and carries a Retry-After header in seconds.
500 server_error The token could not be issued. Try again.

A refresh that answers invalid_grant means the connection has ended, and the business needs to connect again.

When a connection ends

A connection has no end date. It ends when one of these happens.

  • An owner or an admin presses Disconnect on the Zapier card in Settings → Integrations.
  • The person who pressed Allow leaves the business, or their role drops below admin.
  • A used authorization code is presented a second time.

From that moment every token of the connection stops working and its instant triggers are switched off. After Disconnect, and after a used code is presented again, JobField also sends a DELETE request to each subscribed hook address so that Zapier pauses those Zaps. When the connection ends because the person left or their role changed, the Zaps stay on in Zapier but receive no more events, and any Zap step that calls JobField fails until the business connects again.

The token revocation endpoint in the server metadata, /api/oauth/revoke, does not end connections made through the Zapier integration. When the app proves itself, it answers 200 for their tokens and changes nothing. Use Disconnect in JobField instead.

3. Making requests

Send the access token in the Authorization header on every request. JobField never reads a token from the address.

GET /api/v1/me HTTP/1.1
Host: my.jobfield.app
Authorization: Bearer jfat_...
Accept: application/json
  • A request body must be a JSON object of up to 64 KB. An empty body counts as an empty object.
  • Text values are trimmed, and a number is read as text. A value of any other type is treated as missing. A value longer than the field's limit is cut to the limit, except where an endpoint says otherwise, and the limits are listed with each endpoint.
  • Booleans may be sent as true and false, as the numbers 1 and 0, or as the text "true", "yes", "1" and "false", "no", "0" in lower case. Any other value counts as not sent.
  • Every answer carries Cache-Control: no-store.
  • A create can take up to 30 seconds before it answers.
  • JobField records every change made through the API as made by the connection, on behalf of the person who pressed Allow. New records show Zapier as their source. A lead from one of the four lead marketplaces shows the marketplace as its source, with Zapier after it.

Every request is checked in this order. The token exists and has not expired, its connection is still active, the person who connected it is still an owner or an admin, connecting apps is open for the business, the account is not locked, the plan includes Zapier, and the connection is under its rate limit.

4. Errors

Every error from /api/v1 has the same shape.

{
  "error": {
    "code": "validation_error",
    "message": "The arrival window must be between 30 and 480 minutes.",
    "field": "window_minutes"
  }
}

The message is a sentence written for the business owner, and Zapier shows it as it is. The wording may change, so match on code. The field is present when one input caused the error.

Status code When What to do
400 invalid_request The body is not a JSON object, is not valid JSON, could not be read, or is larger than 64 KB. Fix the request.
401 invalid_token The token is missing, malformed, expired, or belongs to a connection that has ended. The answer carries WWW-Authenticate: Bearer error="invalid_token". Refresh the access token. If the refresh fails, the business connects again.
403 plan_required The business's plan does not include Zapier. The owner changes the plan in JobField.
403 account_locked The JobField account is locked. The owner signs in to JobField to see why.
403 forbidden The business has used its monthly allowance of jobs, on POST /jobs. It is also sent when connecting apps is not open for the business. For the job allowance, the owner changes the plan or waits for the next month. When connecting apps is not open, write to support@jobfield.app.
422 validation_error A value is missing or not valid. field names it. Fix the value.
429 rate_limited The connection is over its rate limit, or the same create is already running. The answer carries Retry-After in seconds. Wait that long and try again.
500 internal Something failed on JobField's side. Try again. A create sent with an External ID or a Lead ID is safe to repeat.
503 unavailable JobField could not check the token for a moment. The answer carries Retry-After: 30. Wait and try again. The connection is not affected.

A record that cannot be found is never a 404. A customer or technician named in a create that does not exist in the business is a 422, and a search with no match is an empty list. An address that is not an endpoint answers 404 with a web page instead of JSON, and a method an endpoint does not take answers 405.

5. Rate limits

Limit Value
Requests per connection 120 a minute, counted in fixed one-minute windows
Requests to the token endpoint per address 120 a minute
Refused guesses at the token endpoint per address 30 a minute. After that, codes and tokens JobField never issued are refused with a 429 until the minute is over.
Active instant triggers per business 200

Over a request limit, the answer is a 429 with Retry-After set to the seconds left in the current window, and Zapier waits that long and repeats the step. Past the limit of active instant triggers, POST /hooks answers a 422 on event.

6. Lists and pagination

A list answers with the records in data and a cursor for the next page in next_cursor.

{
  "data": [{ "id": "6f1c2a10-3b4d-4e5f-8a9b-0c1d2e3f4a5b", "name": "Maria Alvarez" }],
  "next_cursor": "WyIyMDI2LTA5LTIxVDE0OjA1OjAwLjAwMFoiLCI2ZjFjMmExMC0..."
}
  • Records come newest first, by the time they were created.
  • limit sets the page size, from 1 to 100, and the default is 25. A larger value is read as 100.
  • cursor takes the next_cursor of the previous page. It is an opaque value, so do not build or change it. A cursor JobField cannot read is ignored, and the first page comes back.
  • next_cursor is null on the last page.

GET /customers, GET /booking-requests and GET /jobs page this way. The service, technician and arrival window lists, the searches and GET /hooks/sample answer in a single page, with next_cursor set to null.

7. Repeated requests

Zapier can run a step again, for example after a timeout or when a person replays a run. Creates are built so that a repeat returns the record the first run made instead of making a second one.

Booking requests: the Lead ID

POST /booking-requests uses lead_id, the ID the lead had where it came from.

  • For a lead marketplace, the platforms yelp, angi, thumbtack and google_local_services, the Lead ID is required and can be up to 200 characters. The lead is filed under that marketplace, so the same lead never lands twice, including when JobField receives it from a direct connection to that marketplace.
  • For facebook_lead_ads and google_ads, and for every other platform when source_name is sent, the key is the platform, the source name and the Lead ID. A repeat with the same key is the same lead, even when other fields changed. Source names are compared without regard to letter case, spaces or punctuation.
  • For website_form, email, other or no platform, without a source_name, two form tools can share the same IDs. There the key is the Lead ID together with every other field of the request except platform_reference, so only an exact repeat counts as the same lead.
  • Without a Lead ID, every request files a new booking request.

A repeat answers 200 with "duplicate": true and the booking request that was filed first. It fills in the name, phone number, email address, address and lead link when they were empty on that request, and adds the keys of platform_reference to the ones already kept, a new value replacing an old one under the same key. Every other field keeps what the first request sent.

Customers and jobs: the External ID

POST /customers and POST /jobs take an optional external_id, the record's ID in the other app.

  • A repeat with the same External ID and the same fields, within 30 days, returns the record made the first time. The answer is a 200, with "matched_existing": true for a customer and "duplicate": true for a job.
  • A request with the same External ID and different fields is treated as a new request. Two Zaps that happen to share an ID never return each other's records.
  • While the first request is still running, a repeat is answered with a 429 and Retry-After: 60.
  • If the first record was deleted, the next request makes a new one.

A customer is also not duplicated by phone or email. POST /customers returns the customer the business already has with the same email address, or the same phone number when it has at least ten digits, and POST /jobs books the job for that customer.

8. Instant triggers

Instant triggers use REST hooks. When a Zap turns on, Zapier subscribes to an event. JobField then sends each new event to the Zap's own address. When the Zap turns off, Zapier unsubscribes.

Events

Event Zapier trigger Sent when Record
booking_request.created New Booking Request A booking request or a message is added, from any source. Booking request
customer.created New Customer A customer is added. Customers brought in by an import are not included. Customer
job.created New Job A job is created. Jobs brought in by an import are not included. Job
job.status_changed Job Status Changed A job moves from one status to another. Creating a job is not a change. Job status change
job.completed Job Completed The work on a job is finished. See the rule below. Job status change
invoice.sent Invoice Sent An invoice reaches the customer for the first time, by email or text, with or without a pay link. Invoice
payment.received Payment Received A payment succeeds. A bank transfer counts once it has settled. Payment
call.completed New Call A call ends, answered or missed. Call

A job counts as completed at each of these changes:

  • It moves from Scheduled or In progress to Done.
  • It moves from In progress to Invoiced, Not paid or Paid.
  • It moves from Scheduled to Invoiced, Not paid or Paid, and the work was started or finished, or the job was due. A job is due when its start time is in the past or less than two hours away.

A deposit taken on a job booked for a later day is therefore not a completed job. A job that is reopened and finished again is sent again.

Subscribe

POST /hooks

Field Required Value
event Yes One of the events above.
target_url Yes The Zap's hook address. It must use https on the host hooks.zapier.com, with no other port, no sign-in details in the address, and at most 2,000 characters.
filters.status No For job.status_changed only. Only changes into this status are sent: scheduled, in_progress, done, invoiced, not_paid, paid or cancelled.
filters.direction No For call.completed only. inbound or outbound.
{
  "event": "job.status_changed",
  "target_url": "https://hooks.zapier.com/hooks/standard/123456/abcdef/",
  "filters": { "status": "done" }
}

The answer is a 201 with the subscription's ID and its signing secret. The secret is shown this once. Keep it with the subscription to check every delivery.

{
  "id": "0d6f3a2e-5b1c-4e8a-9f7d-2c4b6a8e0f13",
  "event": "job.status_changed",
  "secret": "jfwh_..."
}

A 422 names the field that is wrong: event, target_url, status or direction. A business can hold 200 active subscriptions, and past that the answer is a 422 on event.

Unsubscribe

DELETE /hooks/{id}

The answer is always a 200 with {"id": "...", "deleted": true}, including for a subscription that was already removed or never existed. Deliveries still waiting for that subscription are dropped. A business can only remove its own subscriptions.

Sample records

GET /hooks/sample?event=job.status_changed&status=done&limit=3

Parameter Value
event Required. One of the events above.
limit From 1 to 25. The default is 3.
status Optional. For job.status_changed, the same filter as above.
direction Optional. For call.completed, inbound or outbound.

The answer lists recent records in exactly the shape a delivery has, as {"data": [...], "next_cursor": null}. The list can include imported customers and jobs, deleted jobs, and status changes that would not count as a completed job, so it is for testing, not for catching up on events. Zapier shows them when someone presses Test on a trigger. An unknown event is a 422 on event.

Deliveries

Each event is sent as an HTTP POST to the subscription's target_url.

Header Value
Content-Type application/json
User-Agent JobField-Webhooks/1.0
X-JobField-Event The event, for example payment.received.
X-JobField-Event-Id The ID of this delivery. Retries of the same delivery carry the same ID.
X-JobField-Signature The signature described below.

The body is one record, the same shape as the sample records. It is built from the record as it is when the delivery is sent, so it shows the latest values. If the record was deleted before then, nothing is sent.

{
  "id": "7e6d5c4b-3a29-4f18-8e07-d6c5b4a39281",
  "amount": 265,
  "amount_cents": 26500,
  "tip": 20,
  "surcharge": 0,
  "method": "card",
  "method_label": "Card",
  "status": "succeeded",
  "paid_at": "2026-09-24T13:40:00.000Z",
  "job_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "job_number": "JOB-01042",
  "invoice_id": "4c3b2a19-8e7d-4c6b-9a5f-3e2d1c0b9a87",
  "customer_id": "6f1c2a10-3b4d-4e5f-8a9b-0c1d2e3f4a5b",
  "customer_name": "Maria Alvarez",
  "customer_first_name": "Maria",
  "customer_last_name": "Alvarez",
  "customer_phone": "(305) 555-0142",
  "customer_email": "maria.alvarez@example.com",
  "url": "https://my.jobfield.app/crm/jobs/9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
}
  • Deliveries go out every minute, so an event usually arrives within about a minute.
  • The events of one subscription are sent one at a time. When an attempt fails, the rest of that round's events for the subscription wait for the next round, and the failed one waits for its retry. Events can therefore arrive out of order. When order matters, compare the time in the record, such as changed_at or created_at.
  • Any 2xx answer counts as delivered. JobField waits up to 10 seconds for it and does not follow redirects.
  • A delivery is tried up to 7 times. After a failed attempt JobField waits 1 minute, then 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours. After the seventh failure the delivery is marked as not delivered.
  • An answer of 410 Gone switches the subscription off at once, and the deliveries still waiting for it are dropped.
  • A subscription is also switched off after 20 failed attempts in a row with no successful delivery for 24 hours, so a short outage never turns a working Zap off.
  • JobField does not tell Zapier when it switches a subscription off. The Zap stays on in Zapier and receives nothing until it is turned off and on again.
  • Nothing is sent while the business's plan does not include Zapier or its account is locked. Those deliveries are skipped, not saved for later.
  • A delivery can arrive twice, for example when the first attempt timed out after it was received. Use X-JobField-Event-Id to tell a repeat from a new event.

The business owner sees the latest deliveries and their results on the Zapier card in Settings → Integrations.

Checking the signature

Every delivery is signed with the subscription's secret.

X-JobField-Signature: t=1790251200,v1=5f2b0c9e6a...

t is the time of this attempt in Unix seconds, and v1 is the hex HMAC-SHA256 of the text <t>.<body>, keyed with the whole secret as POST /hooks returned it, including jfwh_. To check a delivery:

  1. Read t and v1 from the header.
  2. Compute the HMAC-SHA256 of t, a full stop, and the raw body exactly as it arrived, before any parsing.
  3. Compare the result with v1 in constant time.
  4. Refuse the delivery when they differ, or when t is more than 24 hours away from now.
const crypto = require("crypto");

function verifyJobFieldSignature(secret, rawBody, header, nowSeconds = Math.floor(Date.now() / 1000)) {
  const parts = Object.fromEntries(
    String(header || "").split(",").map((p) => {
      const i = p.indexOf("=");
      return [p.slice(0, i).trim(), p.slice(i + 1).trim()];
    }),
  );
  const t = Number(parts.t);
  if (!Number.isFinite(t) || !parts.v1) return false;
  if (Math.abs(nowSeconds - t) > 24 * 3600) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`, "utf8").digest("hex");
  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(parts.v1, "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

The Zapier integration drops every delivery that does not carry a valid signature for its own subscription.

9. Endpoints

All paths are relative to https://my.jobfield.app/api/v1.

GET /me

Says which business and connection the token belongs to. Zapier calls it to test a connection and to name it.

{
  "business": { "id": "94c9fa88-7e1d-4a39-a0c5-fc0b6bdaf353", "name": "Coastal Home Services" },
  "connection": { "id": "3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c" }
}

The business name is the one the business shows on its documents, or the name it was created with.

GET /service-types

The business's own services, for the Service dropdown. The id is what service takes in a create.

{ "data": [{ "id": "refrigerator", "name": "Refrigerator repair" }], "next_cursor": null }

GET /technicians

Everyone active on the business's team who can be assigned to a job, sorted by name, for the Technician dropdown. This can include owners and dispatchers. Only the ID and the name are returned.

{ "data": [{ "id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "name": "Luis Serrano" }], "next_cursor": null }

GET /booking-windows

The arrival windows a customer can ask for. The first entry is always Anytime. A window's id is its start time and its length in minutes. A business that has not set its own windows gets two-hour windows starting at 10:00, 12:00, 14:00, 16:00 and 18:00.

{
  "data": [
    { "id": "anytime", "name": "Anytime" },
    { "id": "10:00-120", "name": "10am–12pm" }
  ],
  "next_cursor": null
}

GET /customers

The business's customers, newest first, in pages. Deleted customers are left out. Each entry is a customer record, described in section 10.

Parameter Value
limit From 1 to 100. The default is 25.
cursor The next_cursor of the previous page.

A phone or email parameter is refused with a 422, so that a filter is never mistaken for a match. Search with POST /customers/search instead.

POST /customers/search

Finds a customer by phone number or email address. It is a POST so that the phone number and email address travel in the body and never appear in an address or a request log.

Field Value
phone Any format, up to 40 characters. The last ten digits are compared, and at least ten digits are needed.
email Up to 254 characters. Compared without regard to letter case.

Send at least one of the two, or the answer is a 422 on phone. The phone number is tried first, and the email address only when the phone finds nobody. The answer holds at most one customer, or none.

{ "phone": "305-555-0142" }
{ "data": [{ "id": "6f1c2a10-3b4d-4e5f-8a9b-0c1d2e3f4a5b", "name": "Maria Alvarez", "...": "..." }], "next_cursor": null }

POST /customers

Adds a customer. A customer the business already has with the same phone number or email address is returned as it is, and is never overwritten.

Field Limit Notes
first_name 60 characters first_name, last_name or name is required.
last_name 60 characters
name 120 characters The full name. When present, it is used instead of the first and last name.
phone 40 characters Any format.
email 254 characters An address that is not valid is left out, with a warning.
address_line1 200 characters
city 100 characters
state 40 characters
zip 20 characters
notes 2,000 characters
external_id 1,000 characters See section 7.

The answer is the customer record with two more fields. matched_existing is true when an existing customer was returned, and warnings lists the values that were left out. A new customer answers 201, and an existing one answers 200.

{
  "id": "6f1c2a10-3b4d-4e5f-8a9b-0c1d2e3f4a5b",
  "name": "Maria Alvarez",
  "first_name": "Maria",
  "last_name": "Alvarez",
  "phone": "(305) 555-0142",
  "phone_e164": "+13055550142",
  "email": "maria.alvarez@example.com",
  "address_line1": "1450 Brickell Ave",
  "city": "Miami",
  "state": "FL",
  "zip": "33131",
  "source": "zapier",
  "source_label": "Zapier",
  "created_at": "2026-09-21T14:05:00.000Z",
  "url": "https://my.jobfield.app/crm/customers/6f1c2a10-3b4d-4e5f-8a9b-0c1d2e3f4a5b",
  "matched_existing": false,
  "warnings": []
}

GET /booking-requests

The business's booking requests, newest first, in pages, with the same limit and cursor as GET /customers.

POST /booking-requests

Adds a lead to the business's Bookings inbox, the same way a request from the business's own booking page arrives. It is attached to the customer the business already has with the same phone number or email address, and the owner is told about it.

Field Limit Notes
platform Where the lead came from: facebook_lead_ads, google_ads, website_form, email, yelp, angi, thumbtack, google_local_services or other. Any other value is a 422.
source_name 60 characters shown Shown after the platform, for example a form's name. The whole value is used to tell leads apart. It is ignored for the four lead marketplaces.
lead_id 1,000 characters The lead's ID on the platform. Required for the four lead marketplaces, and there up to 200 characters. Send it as text: a number too large to be exact is a 422.
first_name 60 characters
last_name 60 characters
name 120 characters The full name, used instead of the first and last name when present.
phone 40 characters Any format.
email 254 characters An address that is not valid is left out, with a warning.
address_line1 200 characters
city 100 characters
state 40 characters
zip 20 characters
service 120 characters A service id from GET /service-types, or the customer's own words.
issue 4,000 characters What the customer wrote. description is accepted as another name for it.
preferred_date YYYY-MM-DD. A longer value that starts with a date keeps the date. Another format is left out with a warning, and a date that does not exist is left out without one.
preferred_window A window id from GET /booking-windows, or anytime. A value JobField cannot read is left out, with a warning.
urgent A boolean.
lead_url 500 characters A link to the lead on the platform. Only an https link with no sign-in details in it is kept.
reply_email 254 characters A platform address for replying to the lead. It is kept with the lead and never saved as the customer's email.
platform_reference 4 KB An object of other IDs from the platform to keep with the lead. A larger object is not kept.
kind booking, the default, or message for a message that is not a request for a visit.

A lead needs a way to reach the customer. Outside the four marketplaces, a request with no Lead ID, no phone number and no email address is a 422 on phone. A lead with a Lead ID but no phone number or email address is accepted when it carries an https lead link, a platform reference or a reply email. For website_form, email, other or no platform without a source_name, the Lead ID alone is enough, so map a phone number or an email address whenever the form has one.

A lead with no name is filed under its source's name followed by the word customer, for example Zapier customer. A repeat that carries a name replaces it.

The answer is the booking request record with three more fields. duplicate is true for a repeat, matched_existing_customer is true when the lead is attached to a customer, and warnings lists the values that were left out. A new booking request answers 201, and a repeat answers 200.

{
  "id": "2b7e9c44-1d2e-4f3a-9b8c-7d6e5f4a3b2c",
  "kind": "booking",
  "status": "pending",
  "name": "James Whitfield",
  "first_name": "James",
  "last_name": "Whitfield",
  "phone": "(954) 555-0187",
  "phone_e164": "+19545550187",
  "email": "james.whitfield@example.com",
  "address_line1": "88 SE 3rd Ave",
  "city": "Fort Lauderdale",
  "state": "FL",
  "zip": "33301",
  "service": "refrigerator",
  "service_label": "Refrigerator repair",
  "issue": "Fridge is not cooling, freezer is fine.",
  "preferred_date": "2026-09-24",
  "preferred_window": "10am–12pm",
  "booked_for": null,
  "urgent": false,
  "source": "zapier",
  "source_label": "Zapier · Website form",
  "lead_id": "1234567890",
  "customer_id": null,
  "job_id": null,
  "created_at": "2026-09-21T14:05:00.000Z",
  "url": "https://my.jobfield.app/crm/bookings",
  "duplicate": false,
  "matched_existing_customer": false,
  "warnings": []
}

GET /jobs

With number, finds one job by its number. With no number, lists the business's jobs, newest first, in pages. Deleted jobs are left out.

Parameter Value
number The job number in any of its forms, for example 1042, #1042 or JOB-01042. Only the digits are read.
status Optional, for the list. One job status: scheduled, in_progress, done, invoiced, not_paid, paid or cancelled. Any other value is a 422.
limit For the list. From 1 to 100, and the default is 25.
cursor For the list. The next_cursor of the previous page.

A number search answers with at most one job. A number with no digits, or with more than nine, answers with an empty list.

POST /jobs

Creates a job with the status Scheduled. The customer is the one given by customer_id, or else the one the business already has with the same phone number or email address, or else a new customer made from the fields below. When a technician is given, the job is assigned to them and they get a notification. The job counts toward the business's monthly allowance of jobs like any other job.

Field Limit Notes
customer_id A customer ID in this business. An unknown ID is a 422. Either customer_id or a customer name is required.
first_name 60 characters The customer's first name, used when no customer_id is given.
last_name 60 characters
name 120 characters The customer's full name.
phone 40 characters The customer's phone number.
email 254 characters The customer's email address. An address that is not valid is left out, with a warning.
service 120 characters A service id from GET /service-types, or its name. Other words are kept as sent, and a job with no service gets other.
issue 4,000 characters What needs doing.
scheduled_at The start time as an ISO 8601 date and time with its offset, for example 2026-09-24T14:00:00-04:00. A time sent without an offset is read as UTC. Leave it out to create the job without a time.
window_minutes 30 to 480 The arrival window. The default is 120.
technician_id An active technician from GET /technicians. An unknown ID is a 422.
address_line1 200 characters The job's address. When the street, city and ZIP code are all left out, the customer's address is used. A new customer made by this request gets this address too.
city 100 characters
state 40 characters
zip 20 characters
notes 4,000 characters
total 0 to 1,000,000 The job's total in dollars. The default is 0.
external_id 1,000 characters See section 7.

The answer is the job record with two more fields. duplicate is true for a repeat, and warnings lists the values this request left out. A repeat answers with an empty list. A new job answers 201, and a repeat answers 200. When the business has used its monthly allowance of jobs, the answer is a 403 forbidden and no job is made.

10. Records

The same fields come back from a create, a search, a list, a sample and a delivery. Keys are flat, a name comes whole and split into first and last name, an address comes in parts, and every record has a url that opens it in JobField. A value that is not known is null, except that names and the job's technicians are empty text, and a booking request with no window or service shows Anytime and other.

Customer

Field Value
id The customer's ID.
name, first_name, last_name The name, whole and split at the first space.
phone The phone number as the business saved it.
phone_e164 The number as +1 and ten digits when it has ten digits, or eleven starting with 1. Otherwise null.
email The email address.
address_line1, city, state, zip The address.
source, source_label Where the customer came from, as a key and as JobField shows it.
created_at When the customer was added.
url The customer's page in JobField.

Booking request

Field Value
id The booking request's ID.
kind booking or message.
status pending, converted, dismissed or handled.
name, first_name, last_name The name of the person who asked.
phone, phone_e164, email How to reach them. phone can be the platform's relay number when that is all there is, and phone_e164 is set only for the customer's own number.
address_line1, city, state, zip The address.
service, service_label The service as it was given, and its name in the business's list when it has one.
issue What the customer wrote.
preferred_date YYYY-MM-DD, or null.
preferred_window The window as JobField shows it, for example 10am–12pm, or Anytime.
booked_for The time already booked for the visit, when there is one.
urgent true or false.
source, source_label Where the request came from, for example Zapier · Website form.
lead_id The lead's ID on the platform it came from.
customer_id The customer it is attached to.
job_id The job it became, once it is booked.
created_at When it arrived.
url The Bookings inbox in JobField.

Job

Field Value
id The job's ID.
job_number, number The number as an integer and as JobField prints it, for example 1042 and JOB-01042.
status, status_label scheduled, in_progress, done, invoiced, not_paid, paid or cancelled, and its label, for example In progress.
service, service_label The service.
issue What needs doing.
scheduled_at, window_minutes The start time and the arrival window in minutes.
started_at, completed_at When the technician started and finished.
total, total_cents The job's total in dollars and in cents.
technician The main technician's name.
technicians Every assigned technician, separated by commas.
customer_id, customer_name, customer_first_name, customer_last_name The customer.
customer_phone, customer_phone_e164, customer_email How to reach the customer.
address_line1, city, state, zip The job's address.
source, source_label Where the job came from.
created_at When the job was created.
url The job's page in JobField.

Job status change

Sent for job.status_changed and job.completed. It holds every field of the job as it is now, and these fields describe the change.

Field Value
id The job's ID and the change's number, joined by a colon. Each change has its own ID.
job_id The job's ID.
status, status_label The status the job moved to.
previous_status, previous_status_label The status it moved from.
changed_at When it moved.

Invoice

Field Value
id The invoice's ID.
number The invoice number, the job number padded to five digits, for example 01042.
status draft, sent, paid, refunded or void.
amount, amount_cents The amount in dollars and in cents.
sent_at, due_at, paid_at When it was sent, when it is due, and when it was paid.
job_id, job_number The job it belongs to. Here job_number is the number as JobField prints it, for example JOB-01042.
customer_id, customer_name, customer_first_name, customer_last_name, customer_phone, customer_email The customer.
created_at When it was created.
url The job's page in JobField.

Payment

Field Value
id The payment's ID.
amount, amount_cents Everything the customer paid, in dollars and in cents. It includes the tip and the credit card surcharge.
tip The tip in dollars.
surcharge The credit card surcharge in dollars.
method, method_label How the customer paid, as a key and as JobField shows it: card is Card, tap_to_pay is Tap to Pay, bank_transfer is Bank transfer, and cash, check, zelle, venmo and other keep their own names. Keys can be added, so show method_label to people.
status succeeded. Only payments that succeeded are sent.
paid_at When it was paid.
job_id, job_number, invoice_id What it paid for. Here job_number is the number as JobField prints it, for example JOB-01042.
customer_id, customer_name, customer_first_name, customer_last_name, customer_phone, customer_email The customer.
url The job's page in JobField.

Call

Field Value
id The call's ID.
direction inbound or outbound.
status, status_label How the call ended: completed is Answered, no-answer and canceled are Missed, and busy, failed and voicemail keep their own names.
phone, phone_e164 The other side's number.
caller_name The caller's name as the phone network gave it.
line_number The business number the call used.
line_label, line_medium, line_campaign The label, medium and campaign the business set for that number.
customer_id, customer_name, customer_email The matched customer.
job_id The job linked to the call.
started_at, ended_at, duration_seconds When it started, when it ended, and how long it lasted.
answered_by Who picked up an incoming call: Receptionist, Team member or Voicemail, or null when nobody is recorded. Always null on an outgoing call.
summary A short summary of the call, when there is one.
url The call's page in JobField.

11. Changes to this page

Date Change
2026-10-17 First published, describing version 1.0.1 of the Zapier integration.