Authentication
How API keys work across the Charter Boats developer surfaces
Authentication depends on which surface you're calling:
| Surface | Base | Auth |
|---|---|---|
| Discovery API | /api/ai/* | None — public, rate-limited. A key is optional |
| MCP server | /mcp | None — public. An operator's key adds the operator tools |
| Agent API | /api/agent/* | Logged-in agent session — not a keyed surface |
| Operator API — reads | /api/* (GET) | API key on every call; boats, pricing, fees and availability of your companies' boats only — every company your account is a member of |
| Operator API — writes | POST /import/boat, PATCH/DELETE /boats/:id, and your boats' prices, fees, photos and videos, calendar and discounts | API key, plus membership of the boat's company |
| iCal feeds | /calendar/:boatId/export.ics, /calendar/company/:companyId/export.ics | None — calendar apps can't send headers |
The Discovery API, the MCP server and the iCal feeds need no credentials. Everything else under /api/* — boats, pricing, fees, locations, POIs, passages, sea routing, features, manufacturers — expects your key in the X-API-Key header on every request, reads included.
Which surface is built for you?
A keyless surface is not "the free tier" and a key is not "the partner tier". The split is whether your caller can hold a secret at all, and whether you are answering one question or pulling data in bulk.
| You are | Use | Key |
|---|---|---|
| A person browsing charter.boats | The website itself | No — the site calls its own API same-origin |
| An AI assistant, Custom GPT or metasearch engine answering a traveller | Discovery API /api/ai/* | No — optional, and worth sending |
| An MCP host | MCP server /mcp | No — optional: a key spends your own budget, and an operator's key adds the operator tools |
| A calendar app subscribing to a boat's bookings | iCal feed | No — calendar apps cannot send headers |
| A site embedding our search widget | Widget endpoints, GET /api/locations | No — these answer cross-origin |
| An agent, broker or site showing other operators' boats to buyers | Discovery API /api/ai/* | Optional — send it: your own budget and your affiliate ref on every link |
| An operator's server reading its own boats, pricing, fees or availability | Operator & Data API | Yes, on every call |
Two things follow, and they are what people get wrong in both directions:
Don't reach for a key you don't need. If you are an AI agent, a Custom GPT or an MCP host answering one traveller at a time, the Discovery API is built for exactly that and needs no credential — many such callers cannot safely hold one. Sending a key is still worth it, because it credits bookings from your integration to your affiliate profile, but it is never required and an invalid one is ignored rather than refused.
Selling boats you don't operate goes through the Discovery API. The Operator API reads only
the boats of companies you are a member of — what you entered, so you can check it. Another
operator's boat answers 403 there. The Discovery API gives you every boat with the price the guest
pays on charter.boats, and its links send the buyer to us with your affiliate ref. Send your key with
it: without one the budget is shared per IP (or per Custom GPT end user); with one it is your own.
When do you actually need the key?
| Call | Key needed? |
|---|---|
| Any Operator API or Places & Data endpoint, from your server | Yes — on every call |
Import a boat (POST /import/boat) | Yes — and the boat is created under your account |
Delete a boat (DELETE /boats/:id) | Yes — and you must be an admin or owner of the boat's company |
| Edit a boat, its prices, fees, photos, videos, calendar or your discounts | Yes — and you must be a member of the boat's company |
| iCal feeds | No |
Discovery API (/api/ai/*) and /mcp | No — optional (see below) |
What happens without a valid key:
- No key — our bot protection stops the request before it reaches the API and answers with an HTML
403page, not JSON. If you see HTML where you expected JSON, check the header is being sent. - A key that isn't real —
401 Invalid API key. Keys are checked on every call, not just on writes. - A malformed key — treated like no key: bot protection answers the HTML
403. - A boat your company does not operate —
403JSON naming the Discovery API. So isGET /boatswithoutcompany_slugset to your own company.
Every keyed call is recorded against your account, so we can see your usage when you ask about it.
Browsers on other websites
Call the API from your server. Browser requests from another website are not supported: responses carry no CORS headers, so the browser refuses to hand them to your page — and a key in client-side code is a leaked key anyway. The single exception is GET /api/locations (the exact path — the location autocomplete index), which answers cross-origin so the embeddable search widget's location box works. Anything else: proxy it through your own backend.
The Discovery API
/api/ai/* and /mcp never refuse a caller over its key. Sending one is still worthwhile: a valid API key records the call against your account, the boat links in the response carry your affiliate ref when your account has an active affiliate profile, so bookings that start from your integration are credited to you — and it raises your rate limit substantially (see below). An invalid key is ignored — never a 401.
Rate limits
Your key buys one budget, spent across both keyed surfaces — the Discovery API and the keyed read API. Use it wherever you like.
| Caller | Limit |
|---|---|
No key, on the Discovery API or /mcp | 60 requests / 10 min (30 per Custom GPT user), and 200 / day per IP, of which at most 50 / day are single-boat details |
A valid API key, on /api/ai/*, /mcp or the Operator & Data API | 120 requests / minute and 500 / day, of which at most 150 / day are single-boat details (/ai/boats/{id}, its /standouts, and /ai/boats/compare) — higher on request |
| Claude and ChatGPT themselves (calls from Anthropic's and OpenAI's published address ranges) | No per-IP limit — those servers carry every user of the assistant. Counted per platform instead: 20,000 requests / day, of which at most 2,000 are single-boat details |
The daily budgets reset at 00:00 UTC. Every 429 names the limit it hit and links to the same search on charter.boats, so an assistant can send its user to the site.
Every keyed response carries its daily budget in the headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Your daily allowance |
X-RateLimit-Remaining | Calls left today |
X-RateLimit-Reset | Seconds until the budget resets |
Past any limit you get 429 with a Retry-After header — seconds for the per-minute brake, seconds until midnight UTC for the daily ones. The detail limit refuses only detail calls: search keeps answering. Read X-RateLimit-Remaining and pace yourself rather than retrying into the wall.
Need higher limits? Contact us before building around them — we raise them per key.
What you can't do with a key
| Action | Why |
|---|---|
| Book and pay for a charter | Website checkout only — session + Stripe Elements |
| Accept or decline a booking request | Dashboard only — tied to a logged-in session |
The key gives you your own fleet: read it — unpublished boats included, through List Your Fleet — import, update and delete boats, and set their prices, fees, photos and videos, calendar and your company's discounts. Transactional actions — booking, paying, answering a booking request — are tied to a logged-in session on charter.boats and are not exposed as keyed endpoints.
Which companies a key covers
A key belongs to your user account, not to one company. It works for every company your account is a member of: each call checks that you are a member of the boat's company, and deleting a boat needs an owner or admin role there. List Your Fleet without company_id returns the boats of all of them, each row with its company_id; pass company_id to read one. To connect several independent owners, each owner uses their own account and key.
Getting an API Key
Any registered user can generate an API key from the dashboard:
- Log in to charter.boats
- Go to Dashboard → Settings → API
- Read and accept the API usage terms
- Click Generate API Key
- Copy and save your key (it is only shown once)
Regenerating Your Key
If your key is compromised, you can regenerate it from the same settings page. This will immediately invalidate your old key. If the API terms have changed since you accepted them, the page asks you to accept them again before issuing a new key.
Security Best Practices
Never expose your API key in client-side code, public repositories, or anywhere it could be accessed by unauthorized parties.
Recommendations
| Do | Don't |
|---|---|
| Store API keys in environment variables | Commit API keys to version control |
| Use server-side code to make API calls | Include keys in client-side JavaScript |
| Rotate keys periodically | Share keys across different applications |
| Use different keys for dev and production |
Error Responses
Errors are JSON with the status in statusCode and the reason in message (see Response Format). The one exception is a request with no key at all: that is stopped by bot protection and comes back as an HTML 403 page.
401 Unauthorized
| Message | When |
|---|---|
Invalid API key | A key that doesn't belong to any account |
Invalid API key format | A malformed key reached the API (from outside, bot protection usually stops it first with the HTML 403) |
API key required for external requests. Include X-API-Key header. | A request from outside charter.boats reached the API with no key (normally bot protection answers first with the HTML 403) |
API key required. Include X-API-Key header. | POST /import/boat without a key |
Authentication required | An Operator API write (other than the import) with neither a key nor a logged-in charter.boats session |
403 Forbidden
| Message | When |
|---|---|
This key reads only boats your company operates… | Reading a boat, its pricing, fees or availability — or listing GET /boats with a company_slug — when you are not a member of that company — use the Discovery API |
This key lists only your own fleet… | GET /boats without company_slug |
Not a company member | Writing to a boat, or to discounts, of a company you don't belong to |
Admin access required | Deleting a boat in a company where you are a plain member (admin or owner required) |
API key owner is not a member of the specified company | Importing with a company_id you don't belong to |
| (HTML page) | No key sent — bot protection, not the API |