Our approach
CARTT.AI is a multi-tenant platform handling commerce data - orders, customers, payment integrations, accounting connections, inventory feeds. Security is built into the architecture rather than bolted on. This page describes the controls in place and how to report a vulnerability.
Hosting and data residency
- Australian-hosted infrastructure. Tenant data is stored in Australia.
- Commerce data is in a database of its own. Each tenant's orders, customers, products and content live in their own MariaDB database with their own credentials, and a query in one cannot reach another.
- Platform services are not, and we would rather be exact about it. Billing, wallets, subscriptions, support tickets and the tenant registry live in one shared platform database that every tenant deployment connects to, because those are platform-level functions rather than store data. Isolation there is enforced by the application scoping every read and write to the tenant, not by the database handing out separate schemas - so it is a software control, and it belongs in a different sentence from the one above.
- Process isolation depends on how the store is hosted. A tenant on its own domain gets its own cPanel account, its own OS user and CageFS per-user process isolation. A store created on a
*.cartt.aisubdomain without its own domain is built inside a shared platform account, with its own directory, database and credentials but not its own OS user - and CageFS is deliberately switched off on that account, because the tenants on it must be able to read the shared platform code tree. Those stores get the database and application controls described above; they do not get per-tenant process isolation, and we say so rather than let one word cover both cases. A store can be moved onto its own domain and account. - Regular automated backups with off-site retention.
Encryption
- In transit - TLS 1.2+ for all HTTP traffic. HTTP requests are redirected to HTTPS, with one deliberate exception: paths under
/.well-known/, which carry certificate issuance and renewal challenges, the domain-association files payment and platform providers fetch to verify we control a domain, and oursecurity.txt. On our own platform hosts those are served as they arrive, over plain HTTP, so validation can happen before or without a certificate; on tenant storefronts it depends on that site's web server configuration and both arrangements exist. Nothing private is served from there in either case. - Credentials - passwords set on the platform are stored as Bcrypt/Argon2 one-way hashes and are never logged. Where a tenant migrated an existing customer base from an older system, the credentials imported with it are held in the form that system used until that customer next signs in, at which point they are replaced with a modern hash.
- Third-party API credentials - whether a credential is encrypted depends on the field, not on the screen it was typed into, and we would rather draw that line than leave you to discover it. Five groups are encrypted at rest: credentials stored against a dedicated integration connection - marketplaces, shipping and freight carriers, POS terminals, EDI, the accounting and inventory systems a tenant connects, Search Console and Google Ads, video providers - which use Laravel's encrypted casts,
encryptedwhere a single token is stored andencrypted:arraywhere a whole credential set is stored together; the payment-gateway fields our gateway definitions mark secret (the eWAY password, the Fat Zebra token and shared secret, the PayPal client secret, and the Afterpay and Zip API keys and webhook secrets, live and sandbox), which are encrypted on save and decrypted only at the point a payment or a webhook check needs them; and a handful of individual settings encrypted one field at a time as their integrations were built - the Datapel password, and the MYOB client secret, cloud and local passwords and OAuth tokens; and a fourth group that lives against a record rather than on a settings screen - an agency's own SMTP password under the white-label programme, the payment configuration stored against a Wallet, and the PayPal reporting client secret on a connected bank account; and a fifth belonging to the setup wizard - the credentials typed into it are held encrypted in the wizard's own working store, which is emptied when provisioning completes - so an abandoned run leaves what was typed in place until the wizard is finished, and a value written before that store was encrypted is read back as it was stored rather than failing - and provisioning writes the Stripe secret key, the PayPal client secret, the REX API key, the MYOB password and the freight carrier API key into settings encrypted (the SecurePay password is the exception, because the service that charges through it reads plain text). Other credentials entered as a setting are not encrypted: the settings store holds those values in a form the platform can read back, because the systems that consume them require the original. That covers a tenant's SMTP relay password, their SecurePay merchant identifier and password, the REX SOAP API key read by the gift-card and loyalty integration (a different field from the REX API key the wizard writes encrypted), an AI provider key supplied for the storefront chatbot or accounting automation, the reCAPTCHA secret key, the Telegram bot token and webhook secret, the site-audit API key, and the eWAY API key that accompanies the encrypted eWAY password. A gateway secret saved before that field was marked secret may also still be in the older unencrypted form - we read it either way rather than lock a merchant out of their own gateway. Many are never filled in. Those that are sit in the same access-controlled, Australian-hosted database as everything else, reachable by staff of that tenant holding the relevant settings permission - and by two groups outside it: a CARTT.AI platform administrator can issue a one-time link that signs them in as a tenant's administrator for support, provisioning and incident response, and sees the same settings screens while signed in; and where a tenant's account is managed by an agency under our white-label programme, that agency's staff can enter the clients assigned to them the same way, operating as the tenant's own administrator rather than as a platform super-administrator, so the destructive and platform-level actions reserved to us stay reserved. Both are logged. Our admin AI assistant, by contrast, refuses to resolve a credential-shaped setting name at all - so there is no value for it to mask, log or be talked out of. But they are not encrypted at rest, and a blanket sentence should not imply otherwise. Section 7.3 of the Privacy Policy says the same thing and prevails over this page. - Webhook authentication - an inbound webhook that can change anything is authenticated before it is acted on, using whichever scheme the sender publishes. Not every sender publishes a signature: Telegram and our inbound-email receiver authenticate with a shared secret compared in constant time, and both fail closed - an unset secret rejects every request rather than waving it through. Where the sender does sign, we verify the signature. For EDI brokers, POS and inventory systems, and for payment processors such as Stripe, that means HMAC-SHA256 computed over the raw payload with a shared secret. Some processors do it differently and we follow them rather than the label: PayPal signs with a certificate, and verification means submitting the transmission headers and body back to PayPal's verification endpoint for a yes or no. Either way, a webhook that fails verification is rejected. Two endpoints sit outside that sentence and we would rather name them than shelter them under it. The first is inert: eWAY confirms transactions synchronously, when the shopper returns from the hosted payment page, rather than by pushing events - so the
/webhook/ewayaddress exists only for parity with the other gateways, verifies nothing because there is no signing scheme to verify, records the size and source of whatever arrives without keeping the body, and cannot move money, change an order or alter a record. Posting to it achieves nothing. The second is not inert: eBay signs its notifications with an RSA scheme whose public key must be fetched using an application-level token, and that resolver is not built - so an eBay notification currently arrives unverified. The destructive one is treated accordingly: a buyer-erasure instruction, which would irreversibly anonymise a customer's records, is always parked as an audit row for a human to action and is never executed on the strength of an unauthenticated request. Ordinary order and listing notifications are processed, and a platform-level switch flips even those to fail-closed where losing the notifications is preferable to accepting them unverified.
Application controls
- CSRF token validation on session-authenticated forms. A small number of public JSON endpoints cannot carry a session token - a beacon sent from a cached page has none to carry - and are exempt by design. What replaces the token differs by endpoint, and one blanket sentence would flatter the weakest of them, so: the marketing contact form is rate-limited, checked by reCAPTCHA Enterprise and by our own anti-bot service, and carries no signature; the POS register API is authenticated by a device token; inbound webhooks are authenticated by whatever scheme the sender publishes, with the two exceptions named above; the client-error reporter carries a server-issued HMAC ingestion token and a same-origin check, both failing closed, because it is not inert - a report opens or updates a ticket on our bug board, and an unauthenticated one could flood it; and the click-tracking and consent-audit beacons are rate-limited and strictly validated but unauthenticated, because a page served from cache has no credential to carry and adding one would mean not caching the page. Those two are append-only and cannot read anything back, but we would rather not call what they write anonymous: a click session holds the tracking-session id, the customer id where one is signed in, the IP address and the user agent, and a consent record holds the customer id, the page URL, a salted hash of the IP rather than the address itself, and the user agent. What a forged beacon achieves is a false row in a click report, or a consent record for something that did not happen - which is why the authoritative consent state is the visitor's own cookie and this record is the paper trail beside it, not the thing itself.
- SQL injection prevention via parameterised queries (Eloquent ORM).
- Output encoding for XSS prevention.
- Per-route rate limiting for sensitive endpoints (login, contact form, public APIs).
- Optional two-factor authentication on admin accounts.
- Role-based access control with fine-grained permissions across the admin.
- Audit logs for sensitive admin actions.
AI provider isolation
AI processing (Anthropic Claude, OpenAI, ElevenLabs, Black Forest Labs, Google Gemini, fal.ai, Sapling, Stability AI, Runway, and the Kling, Seedance and Hailuo video models) goes through provider APIs. Where a provider offers a commitment not to train its general models on content we send, we have obtained it - but not every provider offers one, and we would rather qualify this than claim a blanket guarantee we cannot give for every provider in that list. Section 7 of the AI Content Disclaimer sets out exactly what we have contracted for and what we have not. A tenant who supplies their own AI provider key does so as a setting, so it falls on the unencrypted side of the line drawn above. Most of these providers process in the United States or the United Kingdom. Three of the video models are Chinese-owned - Kling (Kuaishou), Seedance (ByteDance) and Hailuo (MiniMax) - and they are used only where a merchant picks that model for video generation. How the request reaches them differs by tool, and we would rather be exact: Video Studio goes through fal.ai, and where fal serves a request from the model owner's own infrastructure the image and prompt are processed in China; the Social Video generator posts the image and prompt directly to Kling's own API, so on that path they reach China without an intermediary. Section 7.1 of the Privacy Policy maintains the overseas recipients that matter for where personal information goes, each with its likely country, and prevails over this page - it is a maintained list rather than a closed one, and it says so, because integrations come and go; if the position for a particular provider matters to you, ask us and we will confirm it in writing.
Payment and PCI
CARTT.AI does not store credit card numbers. For shopper checkout, how the card is kept away from us depends on the gateway a merchant has connected (eWAY, PayPal, Afterpay, Zip, Stripe), and we would rather name the three mechanisms than let one word cover them. Most take the shopper to the gateway's own hosted page, so the card is entered on the gateway's site. One tokenises in the browser, sending the card straight from the browser to the gateway and returning only a token to us. And eWAY encrypts the card number and security code in the shopper's browser with eWAY's own public key: what passes through our servers is ciphertext only eWAY can open, forwarded within the same request. That third case is not tokenisation - the encrypted values do transit our servers, they are simply unreadable to us - and calling it tokenisation would overstate our position. Section 7.4 of the Subscription Terms of Service sets all three out and prevails over this page.
There is one path where card data reaches our servers, and it is not a redirected form. Where a merchant's staff key a card into the admin panel to take a phone order (a MOTO order), the card number and security code are submitted to us and forwarded to that merchant's gateway inside the same request. They are never written to a database field: every write to the payment tables passes through a scrubber at the storage boundary that masks card numbers and security codes, and gateway error text is scrubbed before it reaches a log file or an admin screen. This path exists only for staff-entered phone orders, only where the gateway supports them, and it does mean our scope is wider than a purely redirected integration - which is why we describe it here rather than only in the operative terms. Section 7.4 of the Subscription Terms of Service sets it out and prevails over this page.
Incident response
If we become aware of a security incident affecting personal information, we follow Australia's Notifiable Data Breaches scheme: we assess the breach, notify affected tenants and individuals where likely serious harm is identified, and report to the Office of the Australian Information Commissioner.
Responsible disclosure
If you believe you've found a security vulnerability in CARTT.AI, we'd like to hear from you. Please report it to legal@cartt.ai.
We commit to:
- Acknowledging your report within 3 business days.
- Providing a status update within 14 days.
- Crediting you in our changelog (with your permission) once the issue is fixed.
We ask that you:
- Give us reasonable time to investigate and remediate before public disclosure.
- Avoid accessing, modifying or destroying data that isn't your own.
- Avoid actions that could degrade service for other tenants.
- Avoid social-engineering attacks against our staff or customers.
We do not currently run a paid bug bounty programme.
Contact
legal@cartt.ai for security reports. For general privacy questions, see our Privacy Policy.