Privacy Policy
Who this describes
This notice describes the ZENVORA app, the public website, and the account API in this build. It is about the operator of that product. The operator has not published a legal entity.
- Legal entity name
- Owner must complete this before publication.
- Registered address
- Owner must complete this before publication.
- Country of registration
- Owner must complete this before publication.
- Registration number
- Owner must complete this before publication.
The running database is a SQLite file on the computer that runs the API, usually data/zenvora.sqlite. A Supabase migration file exists in the repository. This server does not use Supabase, even if a Supabase address is present in the environment. Do not read Supabase as an active store for ZENVORA.
Account information
When you create an account, the server stores the email, username, display name, a slow hash of the password, and the time the row was created. It stores language choices, theme, haptics, key size, onboarding answers, and similar settings you save later.
It stores a consent row for the Terms of Service and a consent row for this policy, each with version 1.0 and the time you submitted the sign-up form. If the form includes the optional product-news box, it stores that choice too, including a “no”. If an older caller omits the box entirely, no marketing row is written. Opening a legal page does not write a consent row.
Email verification is not implemented. The server does not check that you control the email address. Password reset does not send mail. On this local server, a reset code can be returned in the API response and shown on the reset page. It expires in 30 minutes.
Messages and history
Translation, rewrite, and smart-reply text is stored so history and favourites can work. A row can include the source text, the result, the languages, the provider name, the feature, a tone, and the time. Favourites point at those rows.
The server also stores monthly word counts, daily feature counts, a short rate window, and idempotency rows so the same text within 10 minutes is not counted twice. Deleting history removes the translation rows and those idempotency rows.
Two named product events can be stored in the same SQLite file: onboarding completed, and a plan selected on a screen. That is not an analytics product. No analytics product is active. No marketing email tool is active.
What this build does not collect
This build does not ask for a date of birth, a phone number, a government identity, a photo, contacts, or precise location. It does not offer a parental dashboard. It does not read WhatsApp, notifications, or other apps. The Android overlay, where a device build is allowed to draw it, translates only text you type or paste into its panel.
The public website does not run an analytics tag or a marketing pixel. See the Cookie Policy.
How information is used
The account exists so you can sign in, keep history, count words, and, when Stripe is configured, subscribe. Translation text is used to produce a translation and to show history. Consent rows record the choice you sent at sign-up. Product events record those two named actions. The operator does not use a mailing list.
This draft does not pick a legal basis under a named privacy statute and then claim the product meets that statute. A lawyer has to do that review.
MyMemory and OpenAI
Signed-in translation is POST /v1/translate. The default provider is MyMemory, at api.mymemory.translated.net. The text and the language pair are sent there. MyMemory has no field for style, Arabic variety, or earlier lines, so those are not added to that request. If MYMEMORY_EMAIL is set on the server, that address is sent with the request as MyMemory’s contact parameter. The website does not hold that value.
If OPENAI_API_KEY is set and the server is set to OpenAI, translation is sent to OpenAI instead. That request can include the text, the languages, the style, the Arabic variety, and earlier lines from the same app session as context. Rewrite and smart replies are sent to OpenAI only when you use those features, the plan includes them, and the key is set. If the key is missing, the server returns an error and does not invent a result.
Google, Azure, and DeepL are not connected. The text you submit is sent to whichever provider is active. This policy does not say the text is never shared with a third party.
Stripe
When Stripe is configured, checkout creates a Stripe customer for the account email and a Checkout Session. Card numbers are entered on Stripe’s page, not on ZENVORA. ZENVORA stores the Stripe customer id, subscription id, plan, interval, seats, status, period end, cancel-at-period-end flag, and a short payment error when one is recorded. Webhook events are stored by event id so a repeat can be ignored. The webhook body is not written into the application log line.
The success URL does not unlock Premium. App Store and Google Play billing are not connected.
Hosting, SQLite, and logs
The public website is static files hosted on Vercel. Vercel may keep its own connection logs for that hosting. ZENVORA does not run a separate analytics product on top of those files. The API is not the Vercel site. Accounts stay in SQLite on the operator’s server.
The API writes a log line with the HTTP method, the path, and the status. It does not write the request body, the translation, or the password. An in-memory counter may read the forwarded address so unsigned attempts are limited to 30 a minute. That address is not stored in SQLite and is not part of the log line. The counter lasts about a minute.
The pages also request the Outfit font from Google Fonts. That request leaves ZENVORA’s server and goes to Google. ZENVORA does not set a cookie for it.
Cookies and on-device storage
Details are in the Cookie Policy. These static pages do not set an analytics cookie. When you sign in, the API sets an HttpOnly session cookie. The website does not store the session token in local storage. The phone app uses the device secure store and sends that token as a bearer. An Android development build may also keep a copy in private app preferences, not in the Android Keystore.
Sharing
Text is sent to the active translation provider, as described above. Payment data is handled by Stripe when you start checkout. The public site is hosted on Vercel, and the font file is requested from Google. The operator does not sell account text and does not connect a marketing list.
Those providers are in other countries. This draft does not name a transfer tool or claim that a particular transfer law has been satisfied.
Retention, deletion, and export
The retention page says how long rows stay. You can delete one translation, delete all translation history, export a JSON file of the account data this server holds, or delete the account with the password. Deleting the account removes the SQLite rows for that account, including consent. It does not delete copies already sent to MyMemory, OpenAI, or Stripe.
Accounts and saved translations use the ZENVORA API on this website. Those buttons do not pretend the account changed.
Security and age
Passwords are hashed with scrypt. Session tokens are stored as a SHA-256 hash. There is no security certification. The security page says how to report a problem, without exploit steps.
The product is described as being for people of all ages. It does not check age. This notice does not claim a children’s privacy regime and does not add an age gate.
Your choices
You can refuse the terms and not create an account. You can leave the product-news box unchecked. You can delete history, export the server’s copy of your account, change the password, or delete the account. You can avoid optional features such as rewrite. You cannot, in this build, correct the email on the account or pull a copy back from MyMemory, OpenAI, or Stripe through ZENVORA.
Changes and contact
A new version of this notice gets a new version number. Old consent rows stay as they were. Privacy questions go to zenvorafounder@gmail.com. The server does not send or receive that mail. The same address is the support and security contact.