MySeaBaseAPI v1, read only

API reference

Read your boat's data with an API key, so the owner's office, a management company or tools like Zapier and Make can pull it. Get a signed webhook when something happens on board. Available on the Owner and Private Fleet plans.

API keys

A boat admin creates keys in the app under Settings > Integrations > API keys: a name, and an optional expiry (30 days, 90 days, a year, or never). The key is shown once; MySeaBase keeps only a SHA-256 fingerprint of it and cannot show it again. A boat can have up to 10 live keys. Revoking a key stops it at once. The list shows when each key was created and last used.

A key looks like this:

k.<boat code>.<secret>
k.sea-breeze.Zb3kQ0x9V1mNw2...   (the secret part is 43 characters)

Version 1 keys are read only. A key can read a fixed list of resources (below) and nothing else: it can never write, change users or settings, open files, or read private crew and guest data. Every call made with a key is written to the boat's audit log with the key's id.

Treat a key like a password. Keep it on a server or in your automation tool's secret store, never in a web page or a shared spreadsheet.

Making a call

Every call is one HTTPS POST to https://myseabase.com/api with a JSON body:

{"action": "<action name>", "args": [ ... ], "token": "<your API key>"}

The answer is always JSON: {"ok": true, "data": ...} or {"ok": false, "error": "..."}.

curl -s https://myseabase.com/api \
  -H 'Content-Type: application/json' \
  -d '{"action":"getKasaDurumu","args":[],"token":"k.sea-breeze.Zb3kQ0x9V1mNw2..."}'
{"ok": true, "data": [{"kasaAdi": "Boat EUR", "bakiyeler": [{"doviz": "EUR", "bakiye": 1840.5}]}]}

GET resources

The same reads are also available as plain GET requests, with the key in the Authorization header (never in the address, so it does not end up in logs or browser history):

curl -s https://myseabase.com/api/v1/expenses?month=2027-01 \
  -H 'Authorization: Bearer k.sea-breeze.Zb3kQ0x9V1mNw2...'

The answer has the same {"ok", "data"} shape, with an HTTP status that matches the error (see Errors).

GET /api/v1/...Query parametersAction
boatnonegetBoatProfile
logbookfrom, to (YYYY-MM-DD; default the last 30 days; at most 366 days)listJurnalEntries
expensesstatus (submitted, approved, rejected, reimbursed), month (YYYY-MM), year, category, limit (1 to 200), before (id, for the next page)listExpenses
cashnonegetKasaDurumu
fuelfrom, to, kind (fuel, water), limit, beforelistFuelLog
maintenancenonelistMaintenance
deadlinesdays (1 to 366, default 90)listUpcomingDeadlines
defectsstatus (open, in_progress, waiting_parts, done, cancelled, active, closed), priority, area, limit, beforelistDefects
charterrange (upcoming, past, all, or YYYY-MM), statuslistReservations
crewstatus (active, on_leave, left)listCrew
statisticsfrom, to (default the year to date, at most 3 years)insightsReport
owner-reportmonth (YYYY-MM, default this month)ownerReport
suppliersfilter (all, owed, overdue, credit), category, searchlistSuppliers
payablesstatus (all, open, overdue, due, submitted, paid, cancelled), supplier, categorylistSupplierInvoices
payables-summarynonepayablesSummary

Lists that page (expenses, fuel, defects) return {"items": [...], "next": id or null}: pass next back as before for the following page.

Actions and arguments

With POST, args is a list in this order (leave trailing arguments out to use the defaults):

ActionargsReturns
getBoatProfile[]name, type, modules on, plan, subscription state
listJurnalEntries[from, to]logbook entries, newest day first
listExpenses[{status, month, year, category}, limit, before]expenses with amount, currency, category, vendor, status, receipt file ids
getKasaDurumu[]cash balance per cash box and currency
listFuelLog[{from, to, kind}, limit, before]fuel and water fills
listMaintenance[]maintenance tasks with next due date or engine hours and status (ok, soon, overdue)
listUpcomingDeadlines[days]the due list: maintenance, safety equipment, documents
listDefects[{status, priority, area, limit}, before]defects
listReservations[range, status]charter bookings: dates, party size, status, rate, deposit and balance
listCrew[status]crew list: id, name, role, status, start date, contract end, certificate health
insightsReport[from, to]statistics: days underway, outings, spend, fuel, places, charter, defects
ownerReport[month]the owner's monthly report
listSuppliers[{filter, category, search}]suppliers with balances, overdue and open invoices per currency, and totals (never bank details, contact or notes)
listSupplierInvoices[{status, supplier, category}]supplier invoices with amount, paid, outstanding, overdue, next due and payment schedule (never notes or attachments)
payablesSummary[]owed, overdue and due within a week per currency, by category, counts

Each resource needs its module switched on for the boat; otherwise the answer is Module not enabled. Amounts are strings with two decimals, or integers in cents where the field name ends in Cents. Dates are YYYY-MM-DD; times are Unix seconds. Some field names are Turkish (for example kasaAdi, cash box name, and bakiye, balance) for historical reasons; they are stable.

What a key never sees

Rate limits

60 calls per key per rolling minute. Over that the answer is RATE_LIMITED (HTTP 429 on GET); wait a minute. There is also a per-address limit in front of the API (5 requests a second with short bursts). For a nightly sync, one call per resource is plenty.

Errors

errorGET statusMeaning
AUTH401missing, unknown, revoked or expired key
Bu işlem için yetkiniz yok403the action is not readable with a key
PLAN_REQUIRED403the boat is on the Crew plan; the API comes with Owner and Private Fleet
BOAT_SUSPENDED403the boat's account is suspended
Module not enabled403the boat does not use that module
RATE_LIMITED429more than 60 calls in a minute with this key
a validation message400a bad argument, for example Geçersiz tarih: başlangıç (invalid start date)
Bulunamadı404unknown GET resource

With POST the HTTP status is always 200 and ok tells success from failure.

Webhooks

In Settings > Integrations > Webhooks a boat admin adds up to 5 HTTPS addresses and picks the events each one gets. MySeaBase then POSTs a small JSON message to the address when the event happens (checked about once a minute).

EventWhendata
expense.submittedan expense is enteredid, date, amount, currency, category, status
expense.approvedan expense is approvedsame
defect.openeda defect is reportedid, title, priority, area, status
defect.closeda defect is done or cancelledsame
maintenance.overduea maintenance task becomes overdue (once, until it is done)id, title, nextDueDate, nextDueHours, hoursLeft
charter.confirmeda charter booking is confirmedid, startDate, endDate, partySize, status
logbook.saveda logbook entry is savedid, date
safety.report_raiseda near miss, incident or non-conformity is reported (safety management module)id, kind, occurredDate, status
payable.createda supplier invoice is recorded or accepted (expenses module)id, date, dueDate, amount, currency, category, status, workOrderId
payable.paida supplier invoice is fully paidsame
pingthe Send test buttonmessage

The body never carries names of guests or crew, notes, vendors, pay or anything private: use the ids with the API if you need more.

POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: MySeaBase-Webhooks/1
X-MySeaBase-Event: expense.approved
X-MySeaBase-Delivery: 4182
X-MySeaBase-Timestamp: 1801234567
X-MySeaBase-Signature: sha256=5f0c...e91a

{"boat":"sea-breeze","data":{"amount":"120.00","category":"Marina","currency":"EUR","date":"2027-01-10","id":57,"status":"approved"},"event":"expense.approved","id":"evt_3f1c9a...","occurredAt":"2027-01-10T09:12:47Z"}

Answer with any 2xx status within 10 seconds. id is the same for every delivery of one event (use it to ignore a duplicate); X-MySeaBase-Delivery stays the same across retries of one delivery.

Addresses must be https:// and must resolve to public internet addresses only: private networks (10/8, 172.16/12, 192.168/16), loopback, link-local and the cloud metadata address are refused, when you save the address and again before every delivery. Redirects are not followed.

Verifying a signature

When you add a webhook the app shows its signing secret (whsec_...) once. The signature is HMAC-SHA256 over the timestamp, a dot and the raw body, with the secret as the key, in hex:

X-MySeaBase-Signature: sha256=hex(HMAC_SHA256(secret, timestamp + "." + raw_body))

Compare in constant time, against the raw bytes you received (not re-encoded JSON), and refuse a timestamp older than five minutes.

Python

import hashlib, hmac, time

def verify(secret: str, headers: dict, raw_body: bytes) -> bool:
    ts = headers.get("X-MySeaBase-Timestamp", "")
    sig = headers.get("X-MySeaBase-Signature", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        return False
    mac = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest("sha256=" + mac, sig)

Node.js

const crypto = require('crypto');

function verify(secret, headers, rawBody /* Buffer */) {
  const ts = headers['x-myseabase-timestamp'] || '';
  const sig = headers['x-myseabase-signature'] || '';
  if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const mac = crypto.createHmac('sha256', secret).update(ts + '.').update(rawBody).digest('hex');
  const a = Buffer.from('sha256=' + mac), b = Buffer.from(sig);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: keep the raw body for the check
// app.post('/hook', express.raw({ type: 'application/json' }), (req, res) => {
//   if (!verify(process.env.MYSEABASE_SECRET, req.headers, req.body)) return res.sendStatus(401);
//   const event = JSON.parse(req.body); /* ... */ res.sendStatus(204);
// });

Retries and the delivery log

If your address does not answer with 2xx (an error status, a timeout or no connection), the delivery is tried again after 1 minute, 10 minutes and 1 hour, then given up. The Send test button sends one ping and does not retry. In the app each webhook has a Deliveries log with the last 50 deliveries: time, event, result, HTTP status and attempts.