Migrating from v1 to v3
Key changes, mapping tips, Bulk Migration Sync, and a checklist for moving from the v1 API and OnSched.js to v3.
Choose your path
Not every team rewrites to native /v3/* routes on day one.
- Existing integrations still calling v1-style paths (
/consumer/v1/...,/setup/v1/...): Start with the v1 alias endpoints on v3 guide—swap the API host, keep your v1 bearer token and payloads, then move endpoint-by-endpoint to/v3/*when you are ready. - New integrations or a deliberate full rewrite: Use native
/v3/*endpoints, OAuth2 client credentials (andx-api-keywhere required), following the checklist below.
What changed in v3
- Unified auth model: OAuth2 client credentials for backend integrations and dashboard-issued JWTs/API keys/public client IDs for web flows.
- Expanded linking: Any service can be linked to any location and resource; scope is enforced by IDs instead of global vs location-only services.
- Timezones stored in UTC: Appointments are persisted in UTC to support cross-timezone bookings and multi-day flows.
- Richer availability:
GET /v3/availabilityreturns bothavailableTimesandavailableDays, with optional external calendar sync and round robin controls. - Auditable lifecycle: Holds (
IN), reserved holds (RS), booked (BK), rescheduled placeholders (RE), and cancelled (CN) states are explicit.
Availability mode mapping
- V1 services:
type=1(Appointment) maps to V3availabilityType=schedule(weekly availability);type=2(Event) maps to V3availabilityType=allocation. - V1 resources:
recurringAvailability=truemeans the resource uses weekly availability, whilefalsemeans it uses allocations. - Allocation migration: Allocation-mode services migrate recurring allocation patterns and one-off allocation dates instead of copying service
weeklyAvailability. Allocation-mode resources migrate allocation rows for booking availability and also copy the V1availabilityobject into resourceweeklyAvailabilityso profile and team display screens show the resource's normal weekly hours.
Service field coverage in Bulk Migration Sync
When Bulk Migration Sync creates a V3 service from a V1 service, the following fields are copied (keeping the same name unless noted):
- Identity and display:
name,description,imageUrl,showOnline,legacyId(set from the V1 service ID). - Type / availability:
type→availabilityType(see above), plus weekly availability forschedule-mode services and allocation patterns forallocation-mode services. - Duration:
duration,durationSelect,durationMin,durationMax,durationInterval,padding. - Booking window:
bookAheadUnit,bookAheadValue,bookInAdvance. - Capacity and limits:
bookingLimit,bookingInterval,dailyBookingLimitCount,dailyBookingLimitMinutes,maxBookingLimit,maxResourceBookingLimit, plus V1maxCapacity→ V3bookingsPerSlot. - Round robin: V1 integer
roundRobin→ V3 enum (1→RANDOM,2→BALANCED,3→COMBINED, anything else →NONE). - Fees:
feeAmount,feeTaxable,cancellationFeeAmount,cancellationFeeTaxable,nonRefundable. - Custom fields: V1
customFields.field1–field10→ V3CustomFields(HSTORE, string values only).
V1 service fields without a V3 equivalent are not copied: serviceGroupId, serviceGroupName, calendarId, calendarResourceGroupId, mediaPageUrl, defaultService, consumerPadding, maxGroupSize. Configure equivalent behavior through native /v3/* endpoints after migration.
The sync is idempotent on legacyId: services already migrated are skipped on re-runs. To pick up newly added field coverage on previously migrated services, delete the V3 service (or update it manually) before starting another Bulk Migration Sync for that company.
Endpoint parity and callouts
- The v3 reference highlights any behavioral changes with callouts on each endpoint. If an endpoint is unchanged (for example,
GET /v3/appointments), the v3 path retains v1 behavior but uses the v3 authentication headers. - Validation is stricter in v3. Always fetch availability first, then use those IDs/timestamps when creating or rescheduling appointments.
OnSched.js upgrades
- Swap the script tag to the v3 version and adjust any renamed properties according to the component docs. Core booking flow logic remains the same; availability + appointment endpoints back the widgets.
Bulk Migration Sync (dashboard)
Use this when you need existing V1 companies, locations, services, resources, customers, and appointments copied into v3 in bulk. It is an operator flow with a dashboard JWT, not a substitute for OAuth2 client-credentials API calls.
- Sign in to the Merchant Portal. Open Bulk Migration Sync from Getting Started or API Settings.
- Review V1 companies (
GET /v3/migration/companiesreturns the full list for the signed-in email). Choose which companies to import. The portal pages 20 companies at a time; Select all applies to the full list, not only the current page. There is no “sync every company” default. - Start the job. The portal calls
POST /v3/migration/jobswith{ "v1CompanyIds": ["…"] }and receives202{ "success": true, "jobId": "<uuid>" }within seconds. - Poll
GET /v3/migration/jobs/{id}(dashboard JWT, owning operator only — other operators get404) for jobstatus, per-companypending/running/done/error, counters, anderrors[].GET /v3/migration/jobsreturns the operator's currentpendingorrunningjob, or404if none. - You may close the tab. Work continues on the server. Large sets can take hours. Re-open the modal; it attaches via
GET /v3/migration/jobs.
POST /v3/migration/sync is a thin alias of POST /v3/migration/jobs: same body, same 202 { jobId }. An empty or missing v1CompanyIds array returns 400 — it does not import every company for the operator email, and it does not hold the HTTP request until import finishes. IDs that are not on that operator's V1 company list also return 400.
A second start while that operator already has a pending or running job returns 409 with the existing jobId. The job is completed when every selected company is done or error. A company-level error does not by itself mark the job failed.
Company Sync results, retries, and safe repeats
Each selected company runs a Company Sync for its locations, services, resources, customers, appointments, and business users. Customers are read for every V1 location and de-duplicated by their V1 ID, so a customer shared by locations is copied once. Each company row in the job response includes a result with counts.customersCopied, counts.appointmentsCopied, and structured gaps; the Merchant Portal shows the same counts and actionable gap details as companies finish.
Company Sync is safe to run again for a company that is already in v3. Select that company and start another Bulk Migration Sync to repair an incomplete earlier run. A repeat fills missing entities, attaches a missing customer to an existing appointment, and updates appointment status and hold/blocking metadata. It preserves an existing appointment's time, resources, notes, and customer, and does not overwrite an operator-selected timezone. Existing customer edits are also preserved.
Customers retain the location identified on their V1 customer record and their supported custom fields. A repeat fills a missing location assignment and missing custom field keys without replacing populated V3 values. A customer with no V1 location remains company-wide; use an unfiltered customer list to find these customers. The location used to fetch a customer list does not override the customer's own location.
Repeat syncs also add missing service/resource location links and resource service links. Existing V3 associations are preserved rather than replaced.
Appointments are requested for all five V1 lifecycle statuses: IN (hold), BK (booked), RS (reserved), CN (cancelled), and RE (rescheduled away). BK and RS block availability as appointments; IN blocks as a hold and uses a usable V1 hold expiry or the location's expiration delay. CN and RE are copied for history but do not block availability under the existing rules. An appointment with missing or invalid dependencies, timestamps, or status is reported as a sync gap instead of being silently skipped.
Some V1 appointment history refers to services or resources that were later deleted. Company Sync can recover those dependencies for cancelled/rescheduled-away records and appointments whose end has passed. The dependencies stay archived and unavailable for new bookings; their names remain readable on appointment history. Future holds, booked appointments and reserved appointments that require archived dependencies remain sync gaps. Cancelled and rescheduled-away records may be copied with no resources when V1 supplies an empty resource list; a resource explicitly named by V1 that cannot be resolved remains a gap.
Saved results include service/resource import failures and dependency details where known. An appointment gap may include locationId, status, serviceId and resourceId alongside its V1 appointment ID. These IDs refer to the V1 source data and help identify what needs attention before a repeat sync or cutover.
If an appointment list request fails, Company Sync retries the same list once. After a second failure, it retries inclusive calendar-year windows from 2015 through the end of the next calendar year, retrying each window once. Any windows that still fail are recorded as appointment-list sync gaps with their location, status, and date range, and the sync continues with other locations and statuses. A company is done when its sync ran to completion, even when its result contains gaps; error means the company could not run at all.
For company, location, and resource timezones, named V1 timezone fields take precedence over offsets. Recognized IANA values and known Windows timezone names are mapped deterministically. An offset-only value may use a fixed-offset fallback, but is recorded as a timezone gap when daylight-saving behavior cannot be verified; an unrecognized named timezone or fractional offset is also a gap. On a repeat, the importer repairs a timezone only when the current value still matches the prior importer/default value; a different value chosen in v3 is preserved.
Calendar OAuth authorization, calendar webhooks, and payment data are not part of Company Sync. These integrations must be configured through their respective v3 flows.
Sync depends on the platform being able to reach legacy identity/company credentials—if that fails (for example IP allowlisting to Azure identity endpoints), entity import may be skipped until access is fixed; contact OnSched support in that case. For large or custom migration projects, you can still contact support with v1 company IDs. Resource calendar blocks from v1 (GET /setup/v1/resources/:id/blocks) are copied into v3 as /v3/unavailability/recurringBlock rows (per resource), keyed by legacyId when the v1 block has a stable id.
See Bulk Migration Sync and Company Sync.
Migration checklist (native /v3/* and dashboard)
/v3/* and dashboard)- Inventory credentials: Create v3 client credentials (OAuth2) and API keys/public client IDs in the dashboard. Do not reuse v1 secrets.
- Update callers: Point hosts to
https://v3.onsched.com(orapi-stage.onsched.comfor staging) and apply the new header model (Authorization+x-api-key+ optionalx-client-id). - Verify availability/booking: Run
GET /v3/availabilityfor a known service/location and book it viaPOST /v3/appointmentorPOST /v3/appointment/holdfollowed byPUT /v3/appointment/:id/book. - Handle statuses: Expect holds to expire (
IN), reserved holds to persist (RS), and reschedules to leave anRErecord behind for audit history. - Migrate data (optional): Follow Bulk Migration Sync (dashboard) if you need existing V1 companies copied into v3. Do not wait for a
200fromPOST /v3/migration/sync.
If you are staying on the v1 compatibility layer first, use the testing and rollout guidance there, then return to this checklist when you cut specific flows over to native /v3/*.
With these updates in place, your v1 integration can transition smoothly to the v3 API and widgets.
Updated 6 days ago
