Skip to Content
Welcome to Zendera Knowledge Hub

Locations API

A location is a reusable record for a physical place — typically a pickup or delivery address. The integration API surface for locations covers searching and listing locations, managing the contacts linked to each location, and managing each location’s time windows (recurring opening hours).

These endpoints do not have an interactive Swagger explorer yet (the OpenAPI spec is not bundled in the Knowledge Hub), so all examples below are shown inline.

Location create / update of the address itself is admin-only. From the integration API you can search for existing locations and manage their contacts and time windows; you cannot create new locations directly. Locations get created as a side effect of order import (when you reference a new externalId and address).

Searching and listing locations

Search for one location by name or internal number

GET /v2/locations/search?name=Acme%20warehouse

Or:

GET /v2/locations/search?internalLocationNumber=LOC-warehouse-1

Query parameters

ParameterNotes
nameMatch a location by its name.
internalLocationNumberMatch a location by its internal location number.

Response

{ "id": 999, "name": "Acme warehouse", "instructions": "Ring bell at side gate", "address": "Industrigata 5, 0155 Oslo, NO", "opens": "08:00", "closes": "18:00", "openingHoursIsDefault": false }

Search for multiple locations

POST /v2/locations/search-multiple

Request body

{ "searchTerm": "Acme", "groupIds": [], "page": 1, "perPage": 50 }

Response

{ "locations": [ { "id": 999, "name": "Acme warehouse", "...": "..." } ], "page": 1, "perPage": 50, "total": 17, "totalPages": 1 }

List locations with pagination and sorting

POST /v2/locations/paginated

Request body

{ "searchTerm": "", "groupIds": [], "sortBy": "SORTABLE_FIELD_NAME", "sortDirection": "ASC", "page": 1, "perPage": 100 }

The response is the same shape as search-multiple. Use this endpoint for full-list traversal.

Location contacts

A contact is a person attached to a location — typically a recipient or warehouse contact who should be notified about deliveries, or whose name/phone should be visible to drivers.

List contacts at a location

GET /v2/locations/{locationId}/contacts

Response

{ "contacts": [ { "contactId": 501, "firstName": "Anna", "lastName": "Hansen", "sendNotifications": true, "visibleToDrivers": true } ] }

Add a contact

POST /v2/locations/{locationId}/contacts

Request body

{ "contactId": 501, "sendNotifications": true, "visibleToDrivers": true }

The contactId references an existing contact record (managed via the admin UI).

Update a contact’s notification preferences

PATCH /v2/locations/{locationId}/contacts/{contactId}

Request body

{ "sendNotifications": false, "visibleToDrivers": true }

Remove a contact

DELETE /v2/locations/{locationId}/contacts/{contactId}

Location time windows

Time windows are recurring opening hours for a location, expressed as iCalendar RRule sets — exactly the same format used in recurring orders. Each time window has an RRule and a duration; together they describe when the location is open.

For example, “Open weekdays 08:00–16:00” is one time window with an rruleset of “every weekday at 08:00” and durationSeconds: 28800.

List a location’s time windows

GET /v2/locations/{locationId}/time-windows

Response

{ "timeWindows": [ { "locationTimeWindowId": 7001, "rruleset": "DTSTART:20260101T080000Z\nRRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR", "durationSeconds": 28800 } ] }

Create a time window

POST /v2/locations/{locationId}/time-windows

Request body

{ "rruleset": "DTSTART:20260101T080000Z\nRRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR", "durationSeconds": 28800 }

The response carries the new timeWindow (with its assigned locationTimeWindowId).

Validate a time window before saving

POST /v2/locations/{locationId}/time-windows/validate

Request body

{ "locationTimeWindowId": null, "rruleset": "DTSTART:20260101T080000Z\nRRULE:FREQ=WEEKLY;BYDAY=MO", "durationSeconds": 28800 }

Response

{ "valid": false, "errorCode": "OVERLAP_CONFLICT", "errorMessage": "Overlaps with existing time window 7001", "overlapConflict": { "...": "details..." } }

Validation catches: invalid RRule strings, non-positive durations, and overlap conflicts with existing windows on the same location. Pass an existing locationTimeWindowId if you’re validating an update (so it doesn’t conflict with itself).

Update or delete a time window

PUT /v2/locations/{locationId}/time-windows/{locationTimeWindowId} DELETE /v2/locations/{locationId}/time-windows/{locationTimeWindowId}

The PUT body has the same shape as create.

Common gotchas

  • You can’t create or rename a location via the integration API. Locations get created as a side effect of order import; the admin UI is where you edit them after.
  • Time windows use iCalendar RRules. If your code doesn’t already speak RRule, use a battle-tested library — don’t try to hand-roll the strings.
  • Validate before save. Overlapping time windows fail at save time anyway, but the validate endpoint surfaces the conflict cleanly without creating partial state.

What’s next

  • For attaching custom field values to a location, you currently need to do it in the admin UI — the integration API exposes only the definitions, not the per-location instances.
  • For importing orders that reference these locations, see Importing and recurring orders.
Last updated on