Skip to main content

Move a day's visits to another day

POST/api/v1/calendar/move_day

For a day that cannot be worked, such as one that is rained off. Every visit whose status is scheduled and which starts on from, a day in the account's time zone, moves to to: work visits and quote visits, for everyone, or with membership_id only the visits that person is assigned to. Each keeps its time of day, its length, its crew and its place in a repeat, and keeps the same clock time when the clocks change between the two days. Visits on from that are in progress, completed or cancelled stay where they are and are listed in skipped. It moves visits and books none, so no plan limit is counted. With notify_customers, each customer with an email address gets one email listing their moved visits with the day they were booked for and the new day and times, under the subject "Your visit from [Business] has moved to [New date]". message replaces the standard note; its placeholders are filled in for each customer: [First name], [Customer], [Business], [Your name], [Old date], [New date] and [Visits], the list of their visits, which is added at the end of a note that leaves it out. Each email is listed among the customer's messages as rescheduled. A customer with no email address is moved all the same and listed in not_emailed. These emails are not counted towards the 100 a day that sendQuote is limited to; a workspace can send 300 of them in any 24 hours. A moved visit is reminded the evening before its new day like any other, when the workspace sends visit reminders, unless its customer was emailed here about a new day that is today, tomorrow or the day after: then this email is the reminder and reminder_sent_at is set. All or nothing: if anything is refused, no visit moves and nobody is emailed. Sending the same request again with the same Idempotency-Key returns the first answer and moves nothing further. Managers only. Errors: 422 same_day when from and to are the same day; 422 nothing_to_move when no visit on from is still scheduled; 422 day_too_large when more than 300 visits would move (move a person at a time with membership_id); 422 email_limit_reached when the emails would take the workspace past 300 in 24 hours (send the request again without notify_customers); 422 message_too_long.

  • 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 moveCalendarDay.

Parameters

Headers

  • Idempotency-Keystringrequired

    Unique 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.

  • fromstring (date)required

    The day whose visits move, in the account's time zone.

  • tostring (date)required

    The day they move to. Any other day, earlier or later.

  • membership_idstring (uuid) or null

    Move only the visits this member is assigned to. A visit they share moves with its whole crew.

  • notify_customersboolean

    Email each customer whose visits moved.

    Default: false.

  • messagestring or null

    Replaces the standard note in every email. Placeholders in square brackets are filled in for each customer. Ignored without `notify_customers`.

    Up to 2000 characters.

Responses

200The visits that moved, those that stayed, how many customers were emailed, and those who could not be.

  • dataobjectrequired

    Fields(4)
    • visitsarray of objectrequired

      The visits that moved, at their new times, earliest first.

      Each item(33)
      • lock_versionintegerrequired

        At least 0.

      • created_atstring (date-time)required

      • updated_atstring (date-time)required

      • idstring (uuid)required

      • account_idstring (uuid)required

      • job_idstring (uuid) or nullrequired

      • job_titlestring or null

      • job_lock_versioninteger or null

        At least 0.

      • purposestring

        One of: work, quote_assessment.

      • customer_idstring (uuid)

      • property_idstring (uuid) or null

      • titlestring or null

      • assessment_notesstring or null

      • quote_idstring (uuid) or null

        Current unarchived assessment quote.

      • has_quotesboolean

        Includes archived quotes; customer and property are fixed when true.

      • jobobject

        Full 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_versionintegerrequired

          At least 0.

        • created_atstring (date-time)required

        • updated_atstring (date-time)required

        • idstring (uuid)required

        • account_idstring (uuid)required

        • customer_idstring (uuid)required

        • property_idstring (uuid) or null

        • source_quote_idstring (uuid) or null

        • numberstringrequired

        • titlestringrequired

        • descriptionstring or null

        • statusstringrequired

          One 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".

        • currencystringrequired

        • subtotal_centsinteger (int64)required

          Integer minor currency units, such as pence.

          At least 0.

        • discount_centsinteger (int64)required

          Integer minor currency units, such as pence.

          At least 0.

        • tax_centsinteger (int64)required

          Integer minor currency units, such as pence.

          At least 0.

        • total_centsinteger (int64)required

          Integer minor currency units, such as pence.

          At least 0.

        • internal_notesstring or null

        • completed_atstring (date-time) or null

        • cancelled_atstring (date-time) or null

        • archived_atstring (date-time) or null

        • itemsarray of objectrequired

          Each item(12)
          • idstring (uuid)required

          • service_idstring (uuid) or null

          • positionintegerrequired

          • descriptionstringrequired

          • quantitystringrequired

            Matches ^\d{1,9}(\.\d{1,3})?$.

          • unit_namestring or null

          • unit_price_centsinteger (int64)required

            Integer minor currency units, such as pence.

            At least 0.

          • tax_rate_basis_pointsintegerrequired

            At least 0. At most 10000.

          • tax_inclusivebooleanrequired

          • subtotal_centsinteger (int64)required

            Integer minor currency units, such as pence.

            At least 0.

          • tax_centsinteger (int64)required

            Integer minor currency units, such as pence.

            At least 0.

          • total_centsinteger (int64)required

            Integer minor currency units, such as pence.

            At least 0.

        • repeatsarray of object

          The 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)required

          • intervalintegerrequired

            At 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)required

          • ends_onstring (date) or null

          • season_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_versionintegerrequired

            At least 0.

      • statusstringrequired

        One of: scheduled, in_progress, completed, cancelled.

      • scheduled_start_atstring (date-time)required

      • scheduled_end_atstring (date-time)required

      • actual_start_atstring (date-time) or null

      • actual_end_atstring (date-time) or null

      • instructionsstring or null

      • completion_notesstring or null

      • cancelled_atstring (date-time) or null

      • reminder_sent_atstring (date-time) or null

        When 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)required

      • visit_series_idstring (uuid) or null

      • invoice_idstring (uuid) or null

        Set 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 null

        The repeating visit this visit belongs to, if any.

        Fields(8)
        • idstring (uuid)required

        • intervalintegerrequired

          At 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)required

        • ends_onstring (date) or null

        • season_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_versionintegerrequired

          At least 0.

      • photosarray of objectrequired

        Oldest first.

        Each item(7)
        • idstring (uuid)required

        • filenamestringrequired

        • content_typestringrequired

        • byte_sizeintegerrequired

          At least 1.

        • membership_idstring (uuid) or nullrequired

          Team member who added the photo. They and managers can remove it.

        • urlstring (uri)required

          Signed 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 object

        Everyone clocked in to this visit right now.

        Each item(3)
        • idstring (uuid)required

        • membership_idstring (uuid)required

        • started_atstring (date-time)required

      • time_entryobject or null

        Only from startVisit with `track_time`; the caller's running entry on this visit.

        Fields(19)
        • lock_versionintegerrequired

          At least 0.

        • created_atstring (date-time)required

        • updated_atstring (date-time)required

        • idstring (uuid)required

        • account_idstring (uuid)required

        • job_idstring (uuid) or nullrequired

        • job_titlestring or nullrequired

        • job_numberstring or nullrequired

        • visit_idstring (uuid) or null

        • visit_titlestring or null

        • purposestring

          `work` for job and work-visit time, `quote_assessment` for quote visit time.

          One of: work, quote_assessment.

        • membership_idstring (uuid)required

        • membership_namestringrequired

        • recorded_by_idstring (uuid)required

        • updated_by_idstring (uuid)required

        • started_atstring (date-time)required

        • ended_atstring (date-time) or nullrequired

        • duration_secondsintegerrequired

          At least 0.

        • notesstring or null

      • stopped_time_entryobject or null

        Only from startVisit with `track_time`; the timer stopped elsewhere, or null.

        Fields(19)
        • lock_versionintegerrequired

          At least 0.

        • created_atstring (date-time)required

        • updated_atstring (date-time)required

        • idstring (uuid)required

        • account_idstring (uuid)required

        • job_idstring (uuid) or nullrequired

        • job_titlestring or nullrequired

        • job_numberstring or nullrequired

        • visit_idstring (uuid) or null

        • visit_titlestring or null

        • purposestring

          `work` for job and work-visit time, `quote_assessment` for quote visit time.

          One of: work, quote_assessment.

        • membership_idstring (uuid)required

        • membership_namestringrequired

        • recorded_by_idstring (uuid)required

        • updated_by_idstring (uuid)required

        • started_atstring (date-time)required

        • ended_atstring (date-time) or nullrequired

        • duration_secondsintegerrequired

          At least 0.

        • notesstring or null

    • skippedarray of objectrequired

      Visits on `from` that stayed where they were, and why.

      Each item(2)
      • visit_idstring (uuid)required

      • reasonstringrequired

        The visit's status.

        One of: in_progress, completed, cancelled.

    • emailedintegerrequired

      How many customers were emailed. Zero without `notify_customers`.

      At least 0.

    • not_emailedarray of objectrequired

      Customers whose visits moved and who were not emailed, because they have no email address. Empty without `notify_customers`.

      Each item(2)
      • customer_idstring (uuid)required

      • reasonstringrequired

        One of: no_email.

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/calendar/move_day" \
  -H "Authorization: Bearer $TRADESENDER_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "2026-10-15",
  "to": "2026-10-17",
  "membership_id": "36c7095b-fcec-58a6-b1e1-916bdae515d3",
  "notify_customers": true
}'