Schedule a job visit
POST/api/v1/jobs/{job_id}/visits
A visit added to a completed job makes the job active again, so there is no need to reopen it first. A cancelled job refuses new visits until it is reopened.
- Needs an API key, and runs with the access of the member who made the key.
- Send an
Idempotency-Key. The same key with the same request within 24 hours gives the first answer again and changes nothing. - A connected AI assistant can use it too, as
createJobVisit.
Parameters
In the address
job_idstring (uuid)required
Headers
Idempotency-KeystringrequiredUnique user-scoped key for safely retrying this mutation for 24 hours.
At least 1 character. Up to 200 characters.
Request body
Sent as application/json.
visitobjectrequiredFields(11)
titlestringAssessment title. Work visits use job_title.
Up to 255 characters.
customer_idstring (uuid)Assessment customer.
property_idstring (uuid) or nullAssessment property owned by that customer.
assessment_notesstring or nullInternal notes; never automatically copied to a quote.
Up to 10000 characters.
job_titlestringRename the related job atomically with this visit. Requires job_lock_version and, when editing, the visit lock_version. Missing or invalid versions return 422; stale versions return 409. Omit job_title and job_lock_version when the title is unchanged.
At least 1 character.
job_lock_versionintegerCurrent job version, required with job_title. A stale version rejects the whole save with 409.
At least 0.
scheduled_start_atstring (date-time)requiredscheduled_end_atstring (date-time)requiredinstructionsstring or nulllock_versionintegerAt least 0.
membership_idsarray of string (uuid)
Responses
201Visit details.
dataobjectrequiredFields(33)
lock_versionintegerrequiredAt least 0.
created_atstring (date-time)requiredupdated_atstring (date-time)requiredidstring (uuid)requiredaccount_idstring (uuid)requiredjob_idstring (uuid) or nullrequiredjob_titlestring or nulljob_lock_versioninteger or nullAt least 0.
purposestringOne of: work, quote_assessment.
customer_idstring (uuid)property_idstring (uuid) or nulltitlestring or nullassessment_notesstring or nullquote_idstring (uuid) or nullCurrent unarchived assessment quote.
has_quotesbooleanIncludes archived quotes; customer and property are fixed when true.
jobobjectFull updated job returned when a visit save includes job_title, or when completing this visit also completed its job. Replace the cached job using this record, never by advancing its version from the title metadata alone.
Fields(24)
lock_versionintegerrequiredAt least 0.
created_atstring (date-time)requiredupdated_atstring (date-time)requiredidstring (uuid)requiredaccount_idstring (uuid)requiredcustomer_idstring (uuid)requiredproperty_idstring (uuid) or nullsource_quote_idstring (uuid) or nullnumberstringrequiredtitlestringrequireddescriptionstring or nullstatusstringrequiredOne of: active, completed, cancelled.
billingstringrequired`on_completion`: one invoice once the job is complete. `per_visit`: job lines are the price of one visit, and the job is invoiced batch by batch as visits are completed while it stays active. `none`: "Don’t invoice", for work paid another way or done for free; the job is never invoiced. `monthly`: "At month end". Priced and invoiced exactly as `per_visit` is, but its completed visits are meant to wait until their month is over: a visit is due for invoicing once the day it was booked for is before the first day of the current month, in the account's time zone. createMonthEndInvoices invoices every such job at once.
One of: on_completion, per_visit, none, monthly. Default: "on_completion".
currencystringrequiredsubtotal_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
discount_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
tax_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
total_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
internal_notesstring or nullcompleted_atstring (date-time) or nullcancelled_atstring (date-time) or nullarchived_atstring (date-time) or nullitemsarray of objectrequiredEach item(12)
idstring (uuid)requiredservice_idstring (uuid) or nullpositionintegerrequireddescriptionstringrequiredquantitystringrequiredMatches ^\d{1,9}(\.\d{1,3})?$.
unit_namestring or nullunit_price_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
tax_rate_basis_pointsintegerrequiredAt least 0. At most 10000.
tax_inclusivebooleanrequiredsubtotal_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
tax_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
total_centsinteger (int64)requiredInteger minor currency units, such as pence.
At least 0.
repeatsarray of objectThe repeating visits still running on this job, oldest first: every series with dates left to book. A series for part of the year is listed between its seasons, when the job has no visit booked. A series whose every date is booked is not listed; its visits still carry it.
Each item(8)
idstring (uuid)requiredintervalintegerrequiredAt least 1. At most 99.
unitstringrequired`weekday` is Monday to Friday, one visit per working day, and needs an interval of 1.
One of: day, weekday, week, month.
starts_onstring (date)requiredends_onstring (date) or nullseason_from_monthinteger or null`season_from_month` and `season_to_month` are the first and last month of the part of the year a series runs in, 1 for January to 12 for December, both included. Give both or neither. A season may run over the year end: 11 and 2 is November to February. Dates outside it are skipped; the rhythm is not restarted.
At least 1. At most 12.
season_to_monthinteger or null`season_from_month` and `season_to_month` are the first and last month of the part of the year a series runs in, 1 for January to 12 for December, both included. Give both or neither. A season may run over the year end: 11 and 2 is November to February. Dates outside it are skipped; the rhythm is not restarted.
At least 1. At most 12.
lock_versionintegerrequiredAt least 0.
statusstringrequiredOne of: scheduled, in_progress, completed, cancelled.
scheduled_start_atstring (date-time)requiredscheduled_end_atstring (date-time)requiredactual_start_atstring (date-time) or nullactual_end_atstring (date-time) or nullinstructionsstring or nullcompletion_notesstring or nullcancelled_atstring (date-time) or nullreminder_sent_atstring (date-time) or nullWhen the customer was emailed a reminder of this visit, or was told by moveCalendarDay that it had moved to today, tomorrow or the day after. Null when they have not been, and null again once moveCalendarDay moves the visit without telling them so.
Read only.
membership_idsarray of string (uuid)requiredvisit_series_idstring (uuid) or nullinvoice_idstring (uuid) or nullSet when this completed visit of a job billed `per_visit` or `monthly` is on a live invoice; cleared when that invoice is voided or archived.
seriesobject or nullThe repeating visit this visit belongs to, if any.
Fields(8)
idstring (uuid)requiredintervalintegerrequiredAt least 1. At most 99.
unitstringrequired`weekday` is Monday to Friday, one visit per working day, and needs an interval of 1.
One of: day, weekday, week, month.
starts_onstring (date)requiredends_onstring (date) or nullseason_from_monthinteger or null`season_from_month` and `season_to_month` are the first and last month of the part of the year a series runs in, 1 for January to 12 for December, both included. Give both or neither. A season may run over the year end: 11 and 2 is November to February. Dates outside it are skipped; the rhythm is not restarted.
At least 1. At most 12.
season_to_monthinteger or null`season_from_month` and `season_to_month` are the first and last month of the part of the year a series runs in, 1 for January to 12 for December, both included. Give both or neither. A season may run over the year end: 11 and 2 is November to February. Dates outside it are skipped; the rhythm is not restarted.
At least 1. At most 12.
lock_versionintegerrequiredAt least 0.
photosarray of objectrequiredOldest first.
Each item(7)
idstring (uuid)requiredfilenamestringrequiredcontent_typestringrequiredbyte_sizeintegerrequiredAt least 1.
membership_idstring (uuid) or nullrequiredTeam member who added the photo. They and managers can remove it.
urlstring (uri)requiredSigned link that expires after 24 hours. Refetch the visit for a fresh one; cache images by photo id, not by URL.
created_atstring (date-time)required
running_time_entriesarray of objectEveryone clocked in to this visit right now.
Each item(3)
idstring (uuid)requiredmembership_idstring (uuid)requiredstarted_atstring (date-time)required
time_entryobject or nullOnly from startVisit with `track_time`; the caller's running entry on this visit.
Fields(19)
lock_versionintegerrequiredAt least 0.
created_atstring (date-time)requiredupdated_atstring (date-time)requiredidstring (uuid)requiredaccount_idstring (uuid)requiredjob_idstring (uuid) or nullrequiredjob_titlestring or nullrequiredjob_numberstring or nullrequiredvisit_idstring (uuid) or nullvisit_titlestring or nullpurposestring`work` for job and work-visit time, `quote_assessment` for quote visit time.
One of: work, quote_assessment.
membership_idstring (uuid)requiredmembership_namestringrequiredrecorded_by_idstring (uuid)requiredupdated_by_idstring (uuid)requiredstarted_atstring (date-time)requiredended_atstring (date-time) or nullrequiredduration_secondsintegerrequiredAt least 0.
notesstring or null
stopped_time_entryobject or nullOnly from startVisit with `track_time`; the timer stopped elsewhere, or null.
Fields(19)
lock_versionintegerrequiredAt least 0.
created_atstring (date-time)requiredupdated_atstring (date-time)requiredidstring (uuid)requiredaccount_idstring (uuid)requiredjob_idstring (uuid) or nullrequiredjob_titlestring or nullrequiredjob_numberstring or nullrequiredvisit_idstring (uuid) or nullvisit_titlestring or nullpurposestring`work` for job and work-visit time, `quote_assessment` for quote visit time.
One of: work, quote_assessment.
membership_idstring (uuid)requiredmembership_namestringrequiredrecorded_by_idstring (uuid)requiredupdated_by_idstring (uuid)requiredstarted_atstring (date-time)requiredended_atstring (date-time) or nullrequiredduration_secondsintegerrequiredAt least 0.
notesstring or null
A refusal answers in the one error shape every operation shares.
Example
A request our own tests send. The ids are made up: put your own in their place.
curl -X POST "https://app.tradesender.co.uk/api/v1/jobs/4088bebb-3a71-586f-922c-1612b3fa6557/visits" \
-H "Authorization: Bearer $TRADESENDER_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"visit": {
"scheduled_start_at": "2026-10-13T09:00:00+01:00",
"scheduled_end_at": "2026-10-13T11:00:00+01:00",
"instructions": "The side gate is unlocked.",
"membership_ids": [
"379274bb-b710-5b2f-a775-bf99cae8e0ea"
]
}
}'