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 |
| Agent API | /api/agent/* | Logged-in agent session — not a keyed surface |
| Operator API — reads | /api/* (GET) | API key on every call |
| Operator API — writes | POST /import/boat, DELETE /boats/:id | API key, plus ownership of the boat/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.
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 |
| 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.
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, and 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. An invalid key is ignored — never a 401. All /api/ai/* endpoints share one rate-limit budget; past it you get 429 with a Retry-After header.
What you can't do with a key
| Action | Why |
|---|---|
| Edit an existing listing's details, prices, fees, availability | Dashboard only — tied to a logged-in session |
| Book and pay for a charter | Website checkout only — session + Stripe Elements |
The key gives you read access plus boat import/delete. Transactional and listing-edit actions are tied to a logged-in session on charter.boats and are not exposed as keyed endpoints.
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 | DELETE /boats/:id with neither a key nor a logged-in charter.boats session |
403 Forbidden
| Message | When |
|---|---|
Not a company member | Deleting a boat whose 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 |