Salla's Order Reservations endpoint gives an integration a read-only view of current order reservations across a date window. That makes it useful for calendar, ERP and operations reconciliation, but it is not an availability engine and it does not by itself prevent two systems from promising the same seat or time slot. The safe pattern is to ingest reservations into a normalized local projection, detect interval and capacity conflicts, publish downstream changes through an idempotent outbox, and repeatedly reconcile that projection with Salla.
This is a practical, documentation-based engineering explainer—not a product announcement or news report. The endpoint, scope and response fields below are documented facts from Salla's official Merchant API pages, verified on October 29, 2026. The normalization model, conflict rules, outbox and reconciliation workflow are engineering recommendations. They are not claims about undocumented Salla behavior.
The answer first
Use GET /admin/v2/reservations with a bounded start and end window and an orders.read token. Store each returned reservation using the merchant and reservation ID as identity, normalize the time interval in the merchant's configured time zone, and retain type, seats_count, the order URL and source timestamps. Feed changes to calendars or an ERP through an outbox, then re-read the same window to detect drift.
Do not treat that list endpoint as a command API. The documented operation retrieves current reservations; it does not document creating a reservation, locking capacity or resolving contention. Availability must have one explicit authority, and every other system should be a projection of that authority.
| Concern | Recommended authority | Evidence to retain |
|---|---|---|
| Reservation observation | Salla Order Reservations read endpoint | Reservation ID, type, start, end, seats and source read time |
| Sellable capacity | The product or booking system that owns availability | Capacity version and the decision that accepted the booking |
| Calendar display | Idempotent external projection | Provider event ID, source reservation ID and last applied digest |
| ERP workflow | ERP order/task projection | External document ID and source order reference |
| Drift recovery | Scheduled Salla reconciliation | Window cursor, seen IDs, missing-count threshold and result |
Start with the documented contract
Salla documents GET https://api.salla.dev/admin/v2/reservations as “List Order Reservations.” The page says the endpoint retrieves current order reservations, requires the orders.read scope, and demonstrates start and end date query parameters. The documented response examples contain:
id: reservation identity;title: a display label;type: examples includedayandtime;startandend: the reservation interval;seats_count: the number of seats in that reservation;url: a dashboard order link in the example;created_at: source creation time.
The page was marked modified on September 7, 2026. Separately, Salla's changelog says Order Details and all order webhooks support product_reservations. That is useful context for event-driven discovery, but it does not turn webhook delivery into a complete historical ledger. Use events to reduce latency and the reservations endpoint to reconcile truth over a bounded window.
The public documentation reviewed here does not state that this endpoint:
- writes or cancels reservations;
- guarantees a resource-capacity lock;
- returns a resource ID suitable for conflict grouping;
- specifies whether interval bounds are inclusive or exclusive;
- defines the time-zone offset for every returned timestamp;
- guarantees that one webhook delivery equals one business transition.
Treat those as integration decisions or unknowns. Confirm merchant and product configuration in a controlled environment before allowing automated downstream actions.
Model observation separately from availability
A common failure is to let three systems believe they own the schedule. A storefront accepts a booking, a shared calendar is edited manually, and an ERP creates a work order. Eventually their states diverge.
Use three explicit layers:
- Availability authority: the system that decides whether a seat, technician, room or time slot can be sold.
- Reservation observation: the normalized record read from Salla and linked to its order context.
- Operational projections: calendar events, ERP tasks, notifications and dashboards derived from the observation.
The calendar should not silently become the authority merely because staff can drag an event. If manual calendar edits matter, route them through a reviewed command path that updates the actual availability owner. Otherwise, overwrite or flag the drift during reconciliation.
Build a stable reservation projection
Keep source data and derived data distinct. A provider-neutral record could be:
{
"tenant_key": "merchant-42",
"provider": "salla",
"reservation_id": "1473353380",
"reservation_type": "time",
"title": "Service appointment",
"start_local": "2026-11-03T10:00:00",
"end_local": "2026-11-03T10:30:00",
"source_timezone": "Asia/Riyadh",
"start_utc": "2026-11-03T07:00:00Z",
"end_utc": "2026-11-03T07:30:00Z",
"seats": 1,
"source_order_url": "redacted-or-access-controlled",
"source_created_at": "2026-10-29T13:48:33+03:00",
"observed_at": "2026-10-29T17:05:12Z",
"source_digest": "sha256:..."
}The example is an application design, not a Salla payload. Store the merchant time zone from authoritative configuration and reject ambiguous local times rather than guessing. Convert to UTC for comparison, but preserve original local values for audits and customer-facing display.
Use a compound uniqueness key such as (tenant_key, provider, reservation_id). Reservation IDs from different stores must never collide. Compute the digest only from normalized source fields so a repeated read becomes a no-op.
Define interval and capacity conflict rules
Normalize time reservations to half-open intervals: [start, end). Two intervals overlap when:
a.start < b.end AND b.start < a.endHalf-open boundaries allow one booking to end exactly when the next begins. This is a local comparison rule; do not claim it is Salla's undocumented boundary semantics.
Conflict grouping needs a resource key: product, service, room, staff member or capacity pool. The list example does not expose such a key, so derive it only from an authoritative order/product mapping or explicit configuration. Never group merely by title text.
For a capacity pool, sum seats across overlapping active reservations and compare the maximum concurrent total with the configured capacity. A seats_count value of two consumes two units, not one event. If capacity or resource identity is missing, mark the record unclassified and avoid automatic acceptance or cancellation.
Day reservations require a separate policy. Expand them to the merchant's business-day interval, not the integration server's time zone. Handle daylight-saving transitions for merchants outside Saudi Arabia, and never manufacture a 24-hour UTC duration when the local day is shorter or longer.
Combine webhooks with reconciliation
Webhooks are a fast signal, not the only recovery mechanism. Salla's webhook documentation says the receiver must respond within 30 seconds and describes retries when delivery fails. A receiver should therefore authenticate, durably store the raw event, acknowledge quickly, and process asynchronously.
Recommended flow:
- Verify the webhook according to Salla's current security guidance.
- Persist an immutable inbox record with tenant, event type, source identifiers and payload digest.
- Deduplicate by a stable delivery or business key available in the event model.
- Enqueue a narrow refresh for the affected order or reservation window.
- Upsert the normalized reservation projection.
- Create outbox rows for calendar, ERP and notification consumers.
- Mark the inbox item complete only after durable state is committed.
Order events that include product_reservations can identify likely changes. Still schedule periodic list reads because events can be delayed, duplicated, unavailable during maintenance or missed before an integration was installed.
Reconcile bounded date windows
Avoid one unbounded “read everything” job. Partition by merchant and date window, and keep checkpoints:
window = [today - lookback, today + horizon]
GET reservations?start=window.start&end=window.end
normalize every returned record
upsert by merchant + reservation_id
mark each returned ID as seen in this run
compare local active records inside the same window
quarantine missing records before declaring deletion
emit outbox changes
advance checkpoint only after commitThe lookback covers late status changes; the forward horizon covers upcoming work. Choose both from business lead times, not an arbitrary global constant. Use a grace rule before treating a missing item as removed: a transient API failure, changed filter semantics or incomplete page must not delete calendar events immediately.
Record the request window, HTTP status, result count, run ID and completion time. If the endpoint times out, the read is safe to retry, but the surrounding job must still be idempotent so it does not duplicate downstream events.
Project to calendars and ERP safely
Create one outbox row per destination and reservation digest. An idempotency key such as tenant:reservation:destination:source_digest makes repeated delivery harmless. Store the destination's event or document ID after success.
When a calendar request times out, the outcome is unknown. Query by the stable external key before creating another event. Do not assume the first request failed. For updates, compare the last applied digest and patch only changed fields. For removals, move through a reversible cancelled/tentative state before hard deletion when the destination supports it.
Keep order dashboard URLs in access-controlled records. Do not place them in public calendar descriptions, analytics labels or error messages. Send only the minimum customer information required by the downstream workflow.
Decision table
| Situation | Safe action | Unsafe shortcut |
|---|---|---|
| Same reservation ID and digest | No-op | Rewrite every destination |
| Same ID, changed interval or seats | Update projection and outbox | Create a second calendar event |
| New overlapping reservation below capacity | Project normally and record utilization | Assume any overlap is an error |
| Concurrent seats exceed capacity | Hold automated confirmation and alert owner | Cancel an order based only on local inference |
| Reservation missing in one read | Quarantine and confirm on later read/order detail | Delete immediately |
| Calendar write timed out | Read by external key, then decide | Blindly create again |
| Unknown time zone or resource | Mark unclassified and stop automation | Guess from the integration server |
Best practices
- Give the reservation reader only
orders.read. - Keep tenant identity in every key, log and checkpoint.
- Preserve original local timestamps and convert with an explicit IANA time zone.
- Use half-open intervals consistently in the local conflict engine.
- Count
seats_countagainst capacity instead of counting rows. - Process webhooks through a durable, idempotent inbox.
- Publish calendar and ERP changes through an outbox with read-after-timeout handling.
- Reconcile bounded windows and quarantine missing records before removal.
- Test day, time, boundary, capacity and time-zone cases.
- Expose metrics for reconciliation age, unclassified records, capacity conflicts and outbox lag.
Anti-patterns and limits
- Treating the list endpoint as a reservation lock or write API.
- Using a calendar as a second availability authority.
- Grouping resources by translated title text.
- Assuming every reservation consumes exactly one seat.
- Comparing naive timestamps in the server's local zone.
- Cancelling an order automatically because one reconciliation read omitted it.
- Retrying a timed-out calendar create without checking for the first event.
- Logging bearer tokens, customer data or dashboard URLs.
This design cannot manufacture fields that the provider does not expose. If the integration cannot map a reservation to an authoritative capacity pool, it can still mirror and reconcile the reservation, but it cannot honestly prove that the booking is conflict-free.
Implementation checklist
- Confirm the merchant's authoritative time zone and availability owner.
- Request only the
orders.readscope for the reservation reader. - Fetch a small date window and validate the documented field shapes.
- Build the tenant-scoped normalized projection and source digest.
- Define resource mappings, half-open intervals and capacity rules.
- Add a durable webhook inbox and narrow refresh jobs.
- Add an idempotent outbox for calendar and ERP destinations.
- Reconcile bounded lookback and future windows.
- Quarantine missing and unclassified records for review.
- Test retries, duplicates, uncertain writes, time boundaries and multi-seat overlaps.
Related reading: Salla and Zid order-status state machines, sequential Salla order backfills, Salla webhook endpoint rotation, and the E-commerce Engineering hub.
Primary references
- Salla — List Order Reservations
- Salla — Merchant API changelog
- Salla — Webhooks
- Salla — Register Webhook
- Wikimedia Commons — Computerised Reservation Centre
- Creative Commons — CC0 1.0 Universal
Prepared by: Noor Yasser
Official references
These references document the tools discussed. Examples and design decisions are illustrative and should be adapted to the project and its versions.
Prepared by: Noor Yasser
Working through a similar engineering challenge?
I help teams turn architecture decisions into a clear scope and dependable, reviewable implementation.




