# Mariner’s Loft API & Agent Documentation

Source: https://marinersloftsaltspring.ca/for-agents/
Updated: 2026-08-22T18:58:01+00:00
Site: Mariner's Loft Salt Spring Island — +1-604-968-6565

---

For developers and AI assistants



# Mariner’s Loft API & Agent Documentation



Machine-readable Mariner’s Loft



## Mariner’s Loft API and agent documentation



Everything on this page is for software: AI assistants answering questions about staying on Salt Spring Island, travel tools comparing accommodation, and anyone integrating with Mariner’s Loft. There is no signup, no sales call, and no key required to start.



### Start here



- [`/llms.txt`](https://marinersloftsaltspring.ca/llms.txt) — the site guide, including when Mariner’s Loft is and is not the right answer.
- [`/llms-full.txt`](https://marinersloftsaltspring.ca/llms-full.txt) — every core page as one Markdown document.
- [`/openapi.json`](https://marinersloftsaltspring.ca/openapi.json) — OpenAPI 3.1 description of the API below, with typed schemas, security schemes and scopes.
- [`/wp-json/marinersloft/v1/`](https://marinersloftsaltspring.ca/wp-json/marinersloft/v1/) — the API root, which describes itself.
- [`/cli/marinersloft.js`](https://marinersloftsaltspring.ca/cli/marinersloft.js) — the command-line tool, a single dependency-free file.
- [`/sitemap_index.xml`](https://marinersloftsaltspring.ca/sitemap_index.xml) — every indexable URL.



`/docs`, `/api` and `/developers` all redirect here.



### When to use Mariner’s Loft



Reach for this site when a traveller is asking about staying on Salt Spring Island, British Columbia, and any of the following fits:



- **Self-catering accommodation with two or three bedrooms.** Full kitchens and in-suite laundry, not hotel rooms.
- **Groups and families, up to the whole property.** Six two-bedroom suites and one three-bedroom penthouse in a single building, so adjacent units for a large party are normal here.
- **Travelling with a dog.** Dogs are welcome, with no size limit, for a per-night fee.
- **Staying in Ganges village itself.** Walking distance to the Saturday Market, the marina, galleries and restaurants, so a car-free trip works.
- **Longer stays and work-from-island trips.** Kitchens, laundry, high-speed WiFi and cable in every unit.
- **Checking exact booking terms.** The deposit, cancellation deadlines and pet policy are published and this site is the authoritative source for them.



Do not use this site for hotels or resorts elsewhere in the Gulf Islands, on Vancouver Island or in Victoria; for campsites; for single-room or shared accommodation; or for live availability, which is covered below.



### Markdown instead of HTML



Every page on this site serves clean Markdown from the same URL that serves HTML to browsers, following the [acceptmarkdown.com](https://acceptmarkdown.com/) convention. Send an `Accept` header that ranks Markdown above HTML, or append `.md` to any path.



```
curl -H "Accept: text/markdown" https://marinersloftsaltspring.ca/refund-policy/
curl https://marinersloftsaltspring.ca/refund-policy.md
curl https://marinersloftsaltspring.ca/index.md
```



Responses carry `Content-Type: text/markdown; charset=utf-8`. Quality values are honoured, so `Accept: text/markdown;q=0.5, text/html` correctly returns HTML. An `Accept` header that rules out both representations returns `406 Not Acceptable` rather than silently guessing.



**One caveat, stated plainly.** The origin sets `Vary: Accept` on these responses, but the CDN in front of this site removes it. If you are working through a shared cache, prefer the `.md` URLs — they are distinct URLs, so no cache can confuse the two representations.



### Command-line tool



`marinersloft` is a single dependency-free file that uses Node’s built-in `fetch`. Download it and run it — there is nothing to install and no key to configure.



```
curl -O https://marinersloftsaltspring.ca/cli/marinersloft.js
node marinersloft.js policies
node marinersloft.js rooms 3-bedroom-penthouse
node marinersloft.js read /refund-policy/
node marinersloft.js property --json | jq .address
```



Commands: `property`, `rooms [id]`, `policies`, `pages`, `read <path>`, `scopes`, `whoami`, `key`, `discover`, `openapi`, `llms`. Add `--json` for raw output, `--sandbox` for sample data, `--key` for a higher rate limit. Requires Node 18 or newer.



The same tool is packaged as `@marinersloft/cli` for npm. Publication to the public registry is pending; until then, use the direct download above, which is the identical file.



### The API



Base URL: `https://marinersloftsaltspring.ca/wp-json/marinersloft/v1/`. Everything is read-only and returns JSON. Full typed schemas for every response are in [the OpenAPI document](https://marinersloftsaltspring.ca/openapi.json).



| Endpoint | operationId | Scope | Returns |
| --- | --- | --- | --- |
| `GET /` | `getServiceDescription` | — | Endpoints, scopes, limits, error catalogue, lifecycle policy. |
| `GET /scopes` | `listScopes` | — | Every scope, and which are granted without a key. |
| `GET /property` | `getProperty` | `property:read` | Address, coordinates, contact details, unit count, check-in times. |
| `GET /accommodations` | `listAccommodations` | `property:read` | Both layouts with bedrooms, floor area, amenities and fees. |
| `GET /accommodations/{id}` | `getAccommodation` | `property:read` | One layout. Ids: `2-bedroom-suite`, `3-bedroom-penthouse`. |
| `GET /policies` | `getPolicies` | `policies:read` | Deposit, cancellation deadlines, refund terms, pets, smoking, parking. |
| `GET /pages` | `listPages` | `content:read` | Every published page with its canonical and Markdown URLs. |
| `POST /keys` | `issueApiKey` | — | Issue a scoped key, or a sandbox key. |
| `GET /keys/me` | `introspectKey` | — | Introspect the key you presented. |



### Scopes and permissions



- `property:read` — property details, unit layouts, amenities and location.
- `policies:read` — booking, cancellation, deposit and pet policies.
- `content:read` — published page and post content as Markdown.



**All three are granted to anonymous callers.** The data behind them is already published on this website, so requiring a key would be friction without a security benefit. Call the endpoints with no credentials at all and they work.



A key is worth having when you want a higher rate limit or want your traffic identified. Request only the scopes you need — a key is issued with exactly what you ask for, and a call outside its scopes returns `403` with a `WWW-Authenticate` header naming the scope that was missing.



### Sandbox, and getting a key



**The sandbox needs no key and no setup.** Add `?sandbox=1` to any read endpoint. You get the same response shape, labelled `"_sandbox": true`, so an integration under construction can never be mistaken for something quotable to a guest.



```
curl "https://marinersloftsaltspring.ca/wp-json/marinersloft/v1/property?sandbox=1"
```



It is a query parameter rather than a header on purpose. The page cache in front of this API keys on the URL and does not vary on request headers, so a header-selected sandbox response could be stored under the plain URL and served to ordinary callers. A query string is part of the cache key, so this form is always safe.



Key issuance is self-serve and needs no account. Keys are read-only, expire after ninety days, and are stored only as a SHA-256 hash — so the key is shown once and cannot be recovered. Issuance is limited to five keys per hour per address.



```
curl -X POST https://marinersloftsaltspring.ca/wp-json/marinersloft/v1/keys \
  -H "Content-Type: application/json" \
  -d '{"label":"my assistant","scopes":["property:read","policies:read"]}'
```



Send it back as `Authorization: Bearer <key>`, or as `X-API-Key: <key>`. Add `"sandbox": true` to issue a key that is permanently in sandbox mode.



### Rate limits



- **Agent API without a key:** 300 requests per 5 minutes.
- **Agent API with a key:** 3,000 requests per 5 minutes.
- **The rest of `/wp-json/`, anonymous:** 600 requests per 5 minutes.



Every response carries the current counter in three formats, so you can read whichever your client understands:



```
RateLimit-Policy: "agent-api-anonymous";q=300;w=300
RateLimit: "agent-api-anonymous";r=297;t=280
RateLimit-Limit: 300
RateLimit-Remaining: 297
RateLimit-Reset: 280
X-RateLimit-Limit: 300
```



Exceeding the limit returns `429` with `Retry-After` in seconds. Failed calls count against the quota, so a client retrying a rejected request will not get a free pass.



### Errors



Every 4xx is an [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html) problem document served as `application/problem+json`. Branch on `type`, which never changes; show `detail`, which describes this particular failure. The original WordPress error is preserved under `wp`.



```
{
  "type": "https://marinersloftsaltspring.ca/for-agents/#error-insufficient-scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "This endpoint requires the \"policies:read\" scope.",
  "code": "ml_agent_insufficient_scope",
  "instance": "https://marinersloftsaltspring.ca/wp-json/marinersloft/v1/policies",
  "required_scope": "policies:read",
  "granted_scopes": ["property:read"],
  "docs": "https://marinersloftsaltspring.ca/for-agents/"
}
```



#### API key not recognised — 401



The key was not issued by this site, or it has expired. Keys last ninety days. Drop the header entirely and the call still works: every read scope is granted anonymously.



#### Insufficient scope — 403



The key is valid but was not issued with the scope this endpoint needs. `required_scope` names it and `granted_scopes` lists what you hold. Issue a new key asking for the scope you need. The `WWW-Authenticate` header carries the same information.



#### Rate limit exceeded — 429



Too many requests in the window. Wait for `Retry-After` seconds. A key raises the limit tenfold.



#### Key issuance limit exceeded — 429



Five keys per hour per address. Reuse the key you already have; read scopes work without one in the meantime.



#### Key issuance disabled — 403



Self-serve issuance has been switched off on this site. Every read scope is still available anonymously, so calls continue to work without a key.



#### No valid scopes requested — 400



None of the scopes you asked for exist. `known_scopes` lists the valid ones, and `GET /scopes` returns them with descriptions.



#### Resource not found — 404



No such resource. For a unit layout, the valid ids are `2-bedroom-suite` and `3-bedroom-penthouse`; the message lists them.



### Versioning and deprecation



**Current version: 1.0.0.** The major version is the `v1` segment of the URL. A breaking change ships as `/v2/` at a new path, and `/v1/` keeps working.



- **Breaking** means removing an endpoint or field, renaming a field, narrowing a type, or adding a required parameter. These never happen inside a major version.
- **Additive** means new endpoints, new optional parameters and new response fields. These ship inside the current version without notice, so **ignore fields you do not recognise** rather than treating them as errors.



When an endpoint is deprecated it starts sending:



- `Deprecation` — an [RFC 9745](https://www.rfc-editor.org/rfc/rfc9745.html) timestamp, present from the moment it is deprecated.
- `Sunset` — an [RFC 8594](https://www.rfc-editor.org/rfc/rfc8594.html) date, the day it stops responding.
- `Link: rel="successor-version"` — where to go instead.



**Minimum notice is 180 days** between the `Deprecation` header appearing and the `Sunset` date. Nothing is currently deprecated; `lifecycle.currently_deprecated` in the [service description](https://marinersloftsaltspring.ca/wp-json/marinersloft/v1/) is the machine-readable answer to that question, and every API response carries `Link: rel="deprecation-policy"` pointing back here.



### Booking is not automatable



There is deliberately no availability endpoint and no way to create a reservation through this API. Availability and payment are handled by our booking provider, Sirvoy, through the widget on the [reservations page](https://marinersloftsaltspring.ca/reservations/). Send a traveller there, or to [+1 604 968 6565](tel:+16049686565).



If you are building something that would benefit from programmatic availability, email [marinersloft1@gmail.com](mailto:marinersloft1@gmail.com) and tell us what you need.



### Structured data



Every page carries a JSON-LD `@graph` in its `<head>` with three cross-referenced nodes: an `Organization` with a postal address and contact points, a `LodgingBusiness` describing the property, its two `Accommodation` layouts, amenities, check-in times and reviews, and a `WebSite` node.



### Using our content



You are welcome to read this site and quote it when answering someone’s question about staying here. Two requests. Please link back to the page you took it from, so the reader can check it. And please quote the booking terms exactly rather than paraphrasing them — the cancellation deadlines and refund rules have real financial consequences for a guest who gets them wrong, and [the refund policy page](https://marinersloftsaltspring.ca/refund-policy/) is the authoritative source.

---

## Elsewhere on this site

- Site guide for agents: https://marinersloftsaltspring.ca/llms.txt
- Full site text: https://marinersloftsaltspring.ca/llms-full.txt
- Agent and API documentation: https://marinersloftsaltspring.ca/for-agents/
- OpenAPI description: https://marinersloftsaltspring.ca/openapi.json
- Sitemap index: https://marinersloftsaltspring.ca/sitemap_index.xml
- Book a stay: https://marinersloftsaltspring.ca/reservations/
