Rentshelf logoRentshelf
Menu

Developers: REST API

Last updated: November 3, 2026. Early access: the API is enabled per shop on request. Contact us to enable it for your store.

The Rentshelf API lets your own systems read rental bookings, products, availability and locations, quote and create bookings, change or cancel them, manage blackout dates and webhook subscriptions, and receive signed webhooks when bookings change. The machine-readable OpenAPI 3.1 document is at /api/v1/openapi.json.

Live data. There is no test mode. Every request acts on the shop's real bookings and stock. Creating or changing a booking can email the customer (confirmation and change emails follow the shop's settings) and send_payment_request sends a real Shopify payment link. Try writes on a development store first, and use an Idempotency-Key so retries never create duplicates.

Authentication

Create a key in the Rentshelf admin under Settings > API access. The full key (it starts with rs_live_) is shown once; Rentshelf stores only a SHA-256 hash. Send it from your server as a bearer token. Never embed a key in a storefront, theme or browser code: the API does not send CORS headers.

curl https://rentshelf.app/api/v1/me -H "Authorization: Bearer rs_live_..."

Revoke a key at any time from the same screen; it stops working immediately.

Scopes

Each key carries the scopes chosen when it was created: bookings:read, products:read, availability:read, locations:read, bookings:write, products:write, availability:write, webhooks:write. A write scope also grants the matching read scope. Grant write scopes only to keys that need them.

Rate limits

Each key may make 60 requests per minute (fixed one-minute window), reads and writes combined. Responses include X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). Over the limit you receive HTTP 429 with Retry-After.

Pagination

List endpoints return { "object": "list", "data": [...], "has_more": true, "next_cursor": "..." }. Pass next_cursor as cursor with the same filters to get the next page. limit is 1-100.

Errors

Errors always use the same envelope:

{ "error": { "code": "insufficient_scope", "message": "This API key is missing the bookings:read scope." } }

Codes: invalid_request (400: malformed JSON, unknown field, bad query parameter), unauthorized (401, missing, malformed or revoked key), api_access_disabled, insufficient_scope and plan_limit (403), not_found (404), method_not_allowed (405), conflict, unavailable and idempotency_in_progress (409), payload_too_large (413), unsupported_media_type (415), validation_error and idempotency_conflict (422), rate_limited (429), internal_error (500). Validation errors name the field in param as a JSON path:

{ "error": { "code": "validation_error", "message": "add_ons[0].quantity must be at most 1000.", "param": "add_ons[0].quantity" } }

Write requests

Send a JSON object with Content-Type: application/json, at most 64 KB. Unknown fields are rejected (400) so typos never pass silently. Money is never accepted from the client: prices always come from the product settings in Rentshelf.

Idempotency

Send an Idempotency-Key header (for example a UUID) with every POST and PATCH. For 24 hours, a retry with the same key and the same JSON body returns the original successful response, with the header Idempotent-Replayed: true, instead of creating a second booking. Reusing a key with a different body or endpoint returns 422 idempotency_conflict. A retry while the first request is still running returns 409 idempotency_in_progress; wait a moment and retry. Failed requests (4xx, 5xx) are not stored, so you can fix the request and retry with the same key. Keys are scoped to your shop.

Data format

Fields are snake_case. Dates are YYYY-MM-DD; timestamps are ISO 8601 in UTC. Money is a decimal string such as "120.00" in the booking or shop currency. Statuses are lowercase. New fields may be added without notice; existing fields will not change within v1.

Endpoints

GET /me

Shop domain, currency, timezone, and the calling key's name, prefix and scopes. Useful for testing a key. Scope: any.

curl https://rentshelf.app/api/v1/me -H "Authorization: Bearer $RENTSHELF_API_KEY"

GET /bookings

List bookings, newest first. With updated_since the list is ordered by updated_at ascending, which suits incremental sync. Unpaid checkout holds are excluded. Scope: bookings:read.

Query parameters: status (comma-separated: pending, confirmed, active, returned, cancelled), from and to (YYYY-MM-DD, rental window overlaps), product_id, updated_since (ISO 8601), limit (1-100, default 25), cursor.

curl "https://rentshelf.app/api/v1/bookings?status=confirmed,active&from=2026-12-01&to=2026-12-31&limit=50" \
  -H "Authorization: Bearer $RENTSHELF_API_KEY"

GET /bookings/{id}

Retrieve one booking by its Rentshelf ID. Scope: bookings:read.

curl https://rentshelf.app/api/v1/bookings/BOOKING_ID -H "Authorization: Bearer $RENTSHELF_API_KEY"

GET /products

Rental products with pricing mode and tiers, duration options, add-ons, deposit settings and inventory. Scope: products:read.

Query parameters: active (true/false), limit (1-100, default 50), cursor.

curl "https://rentshelf.app/api/v1/products?active=true" -H "Authorization: Bearer $RENTSHELF_API_KEY"

GET /availability

Available units per day, using the same calculation as storefront checkout: bookings, buffers, blackouts and the product schedule. Both dates are inclusive; at most 93 days per request. Scope: availability:read.

Query parameters: product_id (required), from and to (required, YYYY-MM-DD), variant_id, location_id.

curl "https://rentshelf.app/api/v1/availability?product_id=PRODUCT_ID&from=2026-12-01&to=2026-12-14" \
  -H "Authorization: Bearer $RENTSHELF_API_KEY"

GET /locations

Pickup and stock locations configured in Rentshelf. Scope: locations:read.

curl https://rentshelf.app/api/v1/locations -H "Authorization: Bearer $RENTSHELF_API_KEY"

POST /quotes

Price breakdown and availability for a rental, without saving anything. Uses the storefront pricing engine: duration packages, tiers, weekend and seasonal pricing, add-on limits per duration, minimum notice, booking window, product schedule, min/max duration, and the checkout stock calculation. Fixed-duration products need a duration from the product's duration.options. Scope: availability:read or bookings:write.

Query parameters: product_id, start_date, end_date (required), variant_id, selected_dates, duration { value, unit }, quantity, add_ons [{ id, quantity }], location_id, delivery_method, pickup_time, return_time.

curl https://rentshelf.app/api/v1/quotes -H "Authorization: Bearer $RENTSHELF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"product_id":"PRODUCT_ID","start_date":"2026-12-04","end_date":"2026-12-06","quantity":2,"add_ons":[{"id":"ADDON_ID","quantity":1}]}'

POST /bookings

Create a booking. It is priced and checked exactly like a quote (409 unavailable if stock ran out) and saved like an admin manual booking. With status confirmed (the default) the customer gets the shop's confirmation email if that setting is on; pass send_confirmation_email: false to skip it. send_payment_request: true creates a Shopify payment link for the amount due, emails it to the customer and returns it in payment_request.url. The booking is kept even when the payment request fails; check payment_request.error. Scope: bookings:write.

Query parameters: the quote fields, plus customer { name (required), email, phone }, notes, status (pending | confirmed), send_confirmation_email, send_payment_request.

curl https://rentshelf.app/api/v1/bookings -H "Authorization: Bearer $RENTSHELF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"product_id":"PRODUCT_ID","start_date":"2026-12-04","end_date":"2026-12-06",
       "customer":{"name":"Ada Lovelace","email":"ada@example.com","phone":"+447700900123"},
       "status":"confirmed","send_payment_request":true}'

PATCH /bookings/{id}

Change dates, quantity, variant, add-ons (the full list), delivery method, pickup/return time, notes or status. Item and date changes use the admin booking-change flow: the price is recalculated, stock re-checked, the change saved in the booking history, and any difference tracked as a balance (no money moves). notify_customer: true emails the customer. Status transitions: pending to confirmed or cancelled; confirmed to active, returned or cancelled; active to returned or cancelled. Each change emits the matching webhook event. Manual bookings edited with custom pricing and timed appointments cannot be changed through the API. Scope: bookings:write.

Query parameters: start_date, end_date, selected_dates, duration, quantity, variant_id, add_ons, delivery_method, pickup_time, return_time, notes, status, return_condition (with status returned), reason, notify_customer.

curl -X PATCH https://rentshelf.app/api/v1/bookings/BOOKING_ID -H "Authorization: Bearer $RENTSHELF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"end_date":"2026-12-07","reason":"Extended by one day","notify_customer":true}'

POST /bookings/{id}/cancel

Cancel a booking (same as PATCH with status cancelled). Shopify orders are not refunded or cancelled. Scope: bookings:write.

curl -X POST https://rentshelf.app/api/v1/bookings/BOOKING_ID/cancel -H "Authorization: Bearer $RENTSHELF_API_KEY" -H "Idempotency-Key: $(uuidgen)"

GET /products/{id}

Retrieve one rental product. Scope: products:read.

curl https://rentshelf.app/api/v1/products/PRODUCT_ID -H "Authorization: Bearer $RENTSHELF_API_KEY"

PATCH /products/{id}

Change Rentshelf rental settings only: active, total_units (shared-stock products, not per-variant stock or bundles), min_units, max_units, min_days, max_days. Shopify product data is never changed. Activating a product respects the plan's active product limit (403 plan_limit). Scope: products:write.

curl -X PATCH https://rentshelf.app/api/v1/products/PRODUCT_ID -H "Authorization: Bearer $RENTSHELF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"total_units":6,"max_days":14}'

GET /blackouts

Store-wide and product blackout periods overlapping the range (at most 366 days). Scope: availability:read.

Query parameters: from, to (required), product_id.

curl "https://rentshelf.app/api/v1/blackouts?from=2026-12-01&to=2026-12-31" -H "Authorization: Bearer $RENTSHELF_API_KEY"

POST /blackouts

Block dates for the whole store (omit product_id) or one product. Both dates inclusive; units_blocked defaults to 1 and 0 makes a calendar note. As in the admin calendar, existing blackout days of the same scope inside the range are replaced. Existing bookings are not changed. Scope: availability:write.

Query parameters: product_id, start_date, end_date (required), reason, units_blocked.

curl https://rentshelf.app/api/v1/blackouts -H "Authorization: Bearer $RENTSHELF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"start_date":"2026-12-24","end_date":"2026-12-26","reason":"Holiday","units_blocked":99}'

DELETE /blackouts/{id}

Remove every day of a blackout series. Scope: availability:write.

curl -X DELETE https://rentshelf.app/api/v1/blackouts/BLACKOUT_ID -H "Authorization: Bearer $RENTSHELF_API_KEY"

GET /webhook_endpoints

List webhook endpoints. Secrets are never listed. Scope: webhooks:write.

curl https://rentshelf.app/api/v1/webhook_endpoints -H "Authorization: Bearer $RENTSHELF_API_KEY"

POST /webhook_endpoints

Subscribe a public https:// URL to booking events (REST hooks for Zapier, Make and similar tools). events defaults to all booking events. The signing secret is returned only in this response. At most 10 endpoints per shop. Scope: webhooks:write.

Query parameters: url (required), events, description.

curl https://rentshelf.app/api/v1/webhook_endpoints -H "Authorization: Bearer $RENTSHELF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"url":"https://hooks.zapier.com/hooks/standard/123/abc/","events":["booking.created"]}'

DELETE /webhook_endpoints/{id}

Unsubscribe: delete the endpoint and its delivery history. Scope: webhooks:write.

curl -X DELETE https://rentshelf.app/api/v1/webhook_endpoints/ENDPOINT_ID -H "Authorization: Bearer $RENTSHELF_API_KEY"

Webhooks

Add an endpoint under Settings > API access, or with POST /webhook_endpoints, and choose the events to receive: booking.created, booking.updated, booking.cancelled, booking.picked_up, booking.returned. Use Send test event to receive a ping. Endpoints must be public https:// URLs; redirects are not followed.

Each delivery is a JSON POST whose data.object has the same shape as GET /bookings/{id}; status events also include data.previous_status:

{
  "id": "evt_3f9c...",
  "object": "event",
  "type": "booking.picked_up",
  "api_version": "v1",
  "created_at": "2026-12-01T09:30:00.000Z",
  "shop": "example.myshopify.com",
  "data": { "object": { "object": "booking", "id": "...", "status": "active" }, "previous_status": "confirmed" }
}

Headers: Rentshelf-Event (event type), Rentshelf-Delivery (delivery ID) and Rentshelf-Signature: t=TIMESTAMP,v1=HEX, an HMAC-SHA256 of TIMESTAMP.RAW_BODY using the endpoint signing secret. Respond with any 2xx within 10 seconds. Failed deliveries are retried up to 6 attempts over roughly 11 hours (1 minute, 5 minutes, 30 minutes, 2 hours, 8 hours). After 25 consecutive failures the endpoint is disabled; re-enable it in the admin after fixing it. Deliveries are queued and sent within about five minutes, may arrive more than once and are not strictly ordered, so deduplicate on the event id and fetch the booking when you need the latest state.

Verify a signature (Node.js)

import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.RENTSHELF_WEBHOOK_SECRET; // whsec_...

// Use the raw body: re-serialized JSON will not match the signature.
app.post("/rentshelf/webhooks", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("Rentshelf-Signature") || "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const timestamp = Number(parts.t);
  const body = req.body.toString("utf8");
  const expected = crypto.createHmac("sha256", SECRET).update(`${timestamp}.${body}`).digest("hex");
  const valid = parts.v1 && expected.length === parts.v1.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
  if (!valid || Math.abs(Date.now() / 1000 - timestamp) > 300) return res.status(400).end();

  const event = JSON.parse(body);
  // Deduplicate on event.id, then process asynchronously.
  console.log(req.get("Rentshelf-Event"), event.id, event.data.object.id);
  res.status(200).end();
});

app.listen(3000);

Rotating the signing secret in the admin takes effect for the next delivery attempt.

Usage

Settings > API access shows, for each key, the number of requests today (UTC) and the most recent error response, and, for each webhook endpoint, the share of deliveries that succeeded in the last 7 days.

Zapier

The Rentshelf Zapier integration connects with an API key (paste it when you connect your account; Rentshelf checks it with GET /me and labels the connection with your shop name). Give the key the scopes your Zaps need: webhooks:write and bookings:read for triggers, plus bookings:write or availability:write for actions.

Make

The Rentshelf app for Make uses the same API key connection and REST hooks. Instant triggers (Watch bookings) subscribe a webhook endpoint for the events you choose and remove it when the scenario's webhook is deleted. Modules cover listing, getting and searching bookings, creating bookings, changing status, creating blackouts and quotes. Any other endpoint can be called with the Make an API call module.

Shopify Flow

Rentshelf sends Shopify Flow triggers directly from your store's app installation, with no API key: Rental booked, Rental status changed (any status change, including cancellations, with the previous status), Rental picked up, Rental returned and Rental overdue. Each trigger carries the booking ID and status, product (as a Flow product reference) and title, order name, customer name and email, rental dates, units and a link to the booking. Flow triggers are included in plans with Shopify Flow and are sent within about five minutes of the change.