Apply a batch of LOCATION/SERVICE/STAFF/ARTICLE create, update, and delete operations

Applies a flat list of UPSERT/DELETE operations across LOCATION, SERVICE, STAFF, and ARTICLE entities in one all-or-nothing transaction, with optional cross-entity links (e.g. a STAFF UPSERT linking to a LOCATION created earlier in the same call). Bridges the gap between the single-entity create/update/delete endpoints (one call per entity, no cross-type linking) and the CSV bulk-upload endpoint (one entity type per file, no linking to other types either): use this endpoint when a single caller-side change needs to touch several entities and/or types together and either all of it lands or none of it does.

Processing order. Two passes, not submission order: every UPSERT first (in dependency order LOCATION → SERVICE → STAFF → ARTICLE, so a links reference to an entity created earlier in the same batch always resolves), then every DELETE. The response still mirrors submission order — results[i] corresponds to operations[i] regardless of which pass actually applied it.

entity per operation + entity_type combination:

  • UPSERT LOCATION — same fields as the single-entity location create/update request:
    name (required), external_id (optional — a random one is generated if omitted; matches
    an existing location to update it in place, otherwise creates a new one), type,
    image_url, phone_numbers (list of {phone_number, name}), time_zone (required, IANA
    id), address (required — {address, country_name, zip_code} required, plus region_name,
    city, coordinates: {latitude, longitude}), open_hours (list of
    {day_of_week, slots: [{open_at, close_at}]}), special_dates, custom_fields. Does not
    accept links.

  • UPSERT SERVICEservice_name (required), description (required), external_id,
    service_type, duration, price, virtual_location_url, location_ids (internal
    location ids — not links; a SERVICE's location association is this field), open_hours,
    special_dates, phone_numbers, time_zone, custom_fields. Does not accept links.

  • UPSERT STAFFname (required), external_id, description, photo_url, status,
    gender, languages, job_title, contacts (list of {type, value, description}),
    manager, pronouns, specialities, date_of_birth, date_of_hire, attributes (free-form
    string map), relationships (list of {location_id, service_id}, by internal id — same as
    the single-entity staff endpoint), visit_types (list of {id, name}), tag_ids,
    custom_fields. Additionally accepts operation-level links to LOCATION and/or SERVICE (by
    external_id): a LOCATION link and a SERVICE link both present are paired (cross-product)
    into the staff's relationships; either alone is recorded with the other side unset.

  • UPSERT ARTICLEtype_id (required — an existing article type's id), content
    (required — arbitrary JSON, validated against the type's schema when the type's content type
    is JSON), tags (list of strings). The batch's top-level source_key is required whenever
    any ARTICLE UPSERT is present. Accepts operation-level links to LOCATION, SERVICE, and/or
    STAFF (by external_id — an ARTICLE is never a valid link target); does not accept an
    embedded entity.links object (the single-article endpoint's
    shape) — use the operation-level links array instead. The article's type must currently be
    enabled for the account, and the type's declared link patterns must admit the combination of
    linked entity types submitted.

  • DELETE (any entity_type) — entity is just { "id": "..." }. For LOCATION/SERVICE/
    STAFF, id is the external_id (the same identity UPSERT uses) — a value matching nothing
    is a silent no-op, still reported in the response. For ARTICLE, id is the internal id
    (articles have no external_id). DELETE does not accept links.

Validation rejects the whole batch (no operation applied) when: there are more than 100 operations; two UPSERTs of the same entity type share an external_id; a links entry (or a same-entity-type UPSERT) targets something a DELETE in this same batch also targets — applying every UPSERT before any DELETE makes that ordering unresolvable; a links entry resolves to neither another operation in the batch nor an existing account entity; an ARTICLE UPSERT carries an embedded entity.links; or any single operation fails its own field-level validation (reported as the first offending operations[i]).

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…

Scope

locations-directory-batch-operation:write

Body Params

The list of operations to apply.

operations
array of objects
required
length between 1 and 100

The list of operations to apply, in submission order. Applied all-or-nothing in one transaction: if any operation fails validation, nothing in the batch is written. At most 100 operations per call.

operations*
string
enum
required
length between 1 and 6

UPSERT creates a new entity (matched by external_id) or updates the existing one in place. DELETE removes an existing entity; deletes always run after every UPSERT in the batch, regardless of submission order.

Allowed:
string
enum
required
length between 1 and 8
Allowed:
entity
object
required

The entity payload. Its shape depends on operation and entity_type — see the endpoint description above for the full field reference per combination.

links
array of objects
string
length ≤ 255

Identifies the source document this batch came from. Required only when the batch contains at least one ARTICLE UPSERT (used to derive the internal version token that lets a retried call be recognized instead of double-versioning the articles); ignored otherwise.

Responses

Language
Credentials
OAuth2
Missing 1 required scope
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json