Security
Security in a Manatee integration has four main parts: keep API keys server-side, verify webhook signatures, prevent replay, and keep wallet spending keys out of the integration.
API keys
- Store API keys only on your backend.
- Do not put API keys into frontend JavaScript, mobile apps, screenshots, or public repositories.
- Use testnet keys for testing and mainnet keys only for real payments.
- Rotate a key if it is exposed.
Webhook signature verification
Manatee signs webhook requests with HMAC-SHA256. Verify the signature before changing order state.
Required rules:
- Use the webhook signing secret for the API key that created the payment.
- Verify the raw request body.
- Verify
X-Manatee-SignatureoverX-Manatee-Timestamp + "." + raw request body. - Reject requests whose
X-Manatee-Timestampis outside your allowed clock-skew window. Five minutes is a reasonable default. - Compare signatures with a timing-safe comparison.
- Reject requests with missing or malformed signatures.
See Verify Webhook Signatures for a Node.js example.
Replay protection
Webhook delivery is at-least-once. Your endpoint may receive the same event more than once.
Use X-Manatee-Event-Id for replay protection:
- Verify the signature.
- Check whether
X-Manatee-Event-Idwas already processed. - If it is new, update the order and store the event ID.
- If it was already processed, return
2xxwithout repeating fulfillment.
Payment creation should also be idempotent. Use an Idempotency-Key header for the asset-specific create route. See Idempotency Keys.
Non-custodial wallet handling
Manatee never needs a wallet seed phrase or private spend key. Customer payments go directly to wallet addresses you control.
For BTC and BCH, use a dedicated business wallet account and read-only receive data such as a descriptor or extended public key. For XMR, use a dedicated wallet's primary address and private view key. A view key cannot spend XMR, but it can reveal incoming amounts and wallet history, so treat it as an encrypted secret and never confuse it with the private spend key. Learn more in Receive Descriptors and xpubs and the XMR Stagenet quickstart.
Endpoint hardening
Your webhook endpoint must accept server-to-server HTTP requests. If your site uses Cloudflare, a WAF, bot protection, captchas, browser challenges, or login redirects, bypass those protections only for the webhook path and rely on X-Manatee-Signature verification there.
Do not disable protection for your whole domain.
If a credential may be exposed
- Stop using the affected API key or webhook secret.
- Rotate it from the dashboard.
- Update the server-side integration before restoring checkout traffic.
- Review payments and webhook events created during the affected period.
- Reconcile order state against the API instead of trusting a browser redirect or an unverified callback.
Never send API keys, webhook secrets, receive descriptors, xpubs, XMR view keys, seed phrases, or spend keys in a support email.
New accounts receive an email-verification link, but no credentials by email. The initial Mainnet and Test API keys and their webhook signing secrets are generated for a one-time reveal after the first verified dashboard sign-in. Save them immediately in a secret manager. Manatee retains only API-key hashes; webhook secrets are encrypted at rest because the delivery worker must decrypt them to sign outgoing requests.
Unverified users can request a fresh verification link without exposing whether an account exists. Changing the account email requires independent confirmation links sent to both the current and new address; completing the change revokes all dashboard sessions. Dashboard Settings also provides a recently-authenticated JSON data export and self-service account deletion. Deletion is blocked while a Stripe subscription remains active.
The dashboard identifies API credentials with a shortened SHA-256 fingerprint and records a throttled last-used timestamp without storing plaintext API keys. Each environment also shows the last successful webhook delivery, the latest delivery error, and the last successful synthetic webhook test.
Manatee sends mandatory security notifications after API-key creation or rotation, webhook-secret rotation, password changes, and receive-wallet configuration changes. The account change and notification are committed atomically; an asynchronous outbox worker retries temporary delivery failures and moves exhausted deliveries to a monitored dead-letter state. Notifications identify the affected environment and time but neither the email payload nor the outbox event contains the new credential, a wallet secret, address, descriptor, or token. Password changes and completed password resets revoke all existing dashboard sessions.
Passkeys and confirmation before sensitive actions
A long-lived dashboard session alone is not sufficient to change credentials or payment infrastructure. Manatee requires recent confirmation before it reveals or rotates credentials, changes BTC, BCH, or XMR receive configuration, or opens plan and subscription management. Users can confirm with their current password, a registered passkey, or a one-time recovery code. A successful confirmation remains valid for ten minutes in that browser session.
Passkeys can also sign in without a password. Manatee requires discoverable credentials and user verification, so the authenticator must verify the user with its configured PIN, biometric, or device unlock. Accounts can register multiple passkeys, including platform authenticators and compatible hardware security keys.
The first passkey registration creates ten recovery codes that are shown only once. Each recovery code can confirm one sensitive action from an already authenticated dashboard session; it cannot sign in or create a session by itself. Manatee stores only an account-bound hash of each code. Replacing recovery codes immediately invalidates every previous code.
Every successful confirmation rotates the session token, making the previous token invalid immediately. Passwords are verified server-side and are never stored in the browser. WebAuthn challenges are short-lived, server-side, bound to their ceremony and—when applicable—the current account and session, and consumed once.
Login and password protection
Dashboard password login combines an IP limit with a separate counter for the normalized account email. Failed attempts introduce progressively longer retry windows; after three failures the next password attempt also requires a Cloudflare Turnstile challenge, and ten failures trigger a temporary account-keyed limit. The stored Redis identifiers are SHA-256 digests rather than email addresses. Registration, magic-link emails, and password-reset emails have separate IP and account-keyed limits.
Authentication metrics contain only the login method and outcome. The per-account security history records successful and failed logins for known accounts, but neither mechanism records submitted passwords or Turnstile tokens.
New and changed passwords are hashed with Argon2id using the current application policy. Existing bcrypt hashes remain valid and are replaced atomically with Argon2id after the next successful password login or password confirmation; this migration does not invalidate an otherwise valid session or emit a misleading password-change notification.
Dashboard CSRF protection
Dashboard sessions use cookies, so every state-changing dashboard request is protected against cross-site request forgery. The API requires the X-Manatee-CSRF: 1 request header and verifies the browser's Origin header against the configured dashboard origins. If Origin is unavailable, the API checks Referer; if neither header is present, the request is rejected.
Secure deployments use the host-only __Host-manatee_dashboard_session cookie with Secure, HttpOnly, SameSite=None, Path=/, and no Domain attribute. The __Host- prefix prevents sibling subdomains from setting or overriding the dashboard session cookie. Local HTTP development uses an unprefixed SameSite=Lax cookie because browsers require Secure for __Host- cookies.
Sessions and security activity
The dashboard lists active sessions with a derived browser/device label, an anonymized IPv4 /24 or IPv6 /48 network prefix, creation time, last activity, and absolute expiry. Users can sign out one session or all sessions except the current one. Session removal requires recent authentication.
Dashboard sessions have a 30-day absolute lifetime and a 30-minute server-side idle timeout. The security history records a fixed set of relevant login, password, passkey, recovery-code, credential, receive-configuration, billing, and session events. It deliberately excludes API keys, webhook secrets, recovery codes, session and login tokens, wallet addresses, descriptors, xpubs, view keys, and request payloads. Mandatory outbox-backed security emails are sent when passkeys are added or removed and when recovery codes are replaced.
Reporting a security issue
Send a concise report to contact@manatee-api.io. Include the affected endpoint or component, the time observed, reproducible steps, and non-secret request identifiers where available. Do not include credentials or wallet secrets.
The public beta does not claim SOC 2, ISO 27001, PCI DSS, an external security audit, or a contractual incident-response SLA. Hosting scope is documented in Hosted and Operated in Germany, and personal-data retention is described in the Privacy Policy.