Version 2026-08-26.1, last updated 26 August 2026.
⚠️ Draft. This document was prepared from the product's source code and database schema by the people who built it, not by a lawyer. It has not yet been reviewed by counsel. See
COUNSEL_REVIEW.mdin the same folder for the open questions. Do not publish this to real users until that review happens.
This policy describes what vdaDocs actually does with your information. It was written by reading the code and the database, not by filling in a template. If you find something here that does not match what the app does, that is a bug and we want to hear about it.
1. Who we are
vdaDocs is a visa readiness tool. It tells you what a given government asks of an applicant applying from India (which forms, which documents, which pitfalls) and lets you track how ready you are before you pay a fee.
We are not a visa agency. We never file an application on your behalf, we have no affiliation with any government or consulate, and we never handle your application documents. That is a product decision, and it is also why this policy is short: we simply do not hold the kind of information a filing agent holds.
🔲 Owner decision required: the legal entity name, registered address, and the country of establishment are not yet settled and are left blank here deliberately. A privacy policy with an invented controller is worse than no policy. See
COUNSEL_REVIEW.md.
Data controller: [ENTITY NAME AND REGISTERED ADDRESS: TO BE COMPLETED] Contact: [CONTACT EMAIL: TO BE COMPLETED]
2. What we never collect
We want to be as clear about the absences as about the presences, because for this product the absences are the point.
- We never collect your passport number, or any other government document number. There is no field for one anywhere in the app or the database.
- We never take your documents. The app has no camera access, no photo picker, no file picker and no upload of any kind. Our servers have no file upload capability installed at all. When your checklist says you have your bank statements, we store a tick (a boolean) and never the statement.
- We never file anything for you. Our servers make no outbound call to any government system, ever.
- We do not ask for your name, phone number, postal address, gender,
nationality, income, employer, or marital status. (An optional
namefield exists in our database from an earlier version; the current app never asks for one and never sends one.) - We do not sell your personal information, and we do not share it for cross-context behavioural advertising.
- We do not use advertising SDKs, attribution/install-tracking SDKs, session replay, or ad tracking pixels. None are installed.
3. What we collect, why, and on what legal basis
3.1 Your account
| What | Why | Legal basis (GDPR) |
|---|---|---|
| Email address | It is your login and the only way to reset your password | Contract (Art. 6(1)(b)) |
| Password, stored only as a scrypt hash | To sign you in. We never store the password itself and cannot recover it | Contract |
| Date of birth | To determine whether you are under 18, so that we can exclude minors from marketing contact | Legal obligation / legitimate interest in child safety (Art. 6(1)(c), 6(1)(f)) |
A derived is_minor flag |
The single thing the date of birth is used for | as above |
| Account creation and update timestamps | Account administration and security | Contract / legitimate interest |
Date of birth is mandatory at signup and is collected from everyone. This is a change from earlier versions of the app, which is why we say "collect" rather than "may collect". We use it for exactly one thing: deciding whether you are a minor. We do not use it for age-based content, personalisation, or advertising.
Where we store the year but not the point: we store the full date, not just
a year. If counsel prefers, storing only the year of birth would serve the same
purpose with less data. That change is listed in COUNSEL_REVIEW.md.
3.2 Your plans and checklists
When you create a plan, we store: the country you are applying from (currently always India), the destination country, the visa category and route, the specific forms you selected, and your optional target travel date. We also store which checklist items you have ticked, as item identifiers and a true/false, never the content of any document.
Plans sync so you can use the same account on more than one device and not lose your progress. Legal basis: contract; this is the service.
If you use the app without an account, your plans live only on your device, keyed to a random per-install token, until you sign in.
3.3 Bookmarks and saved articles
These never leave your device. There is no bookmarks table on our servers.
3.4 Reading behaviour, used only on your device
The app ranks your news feed against what you have actually been reading. To do that it keeps, on your phone and nowhere else, a short list of at most 60 country codes for stories you opened.
That list is never uploaded. There is no endpoint that accepts it and no code that sends it. We built it this way on purpose: personalisation does not require a server to know you.
3.5 Usage and crash reporting, behind a consent switch
Settings → Privacy has a single switch controlling all three measurement sinks below. Turning it off stops all of them, empties any queued events on your device, and clears the account identifier we gave to crash reporting.
⚠️ This switch is currently ON by default and there is no first-run prompt. We are telling you that plainly rather than burying it. For users in the EU/EEA and the UK, an opt-out default is very likely not a valid basis for non-essential analytics; this is flagged as a required fix in
COUNSEL_REVIEW.mdand this sentence must be removed once it is fixed.
(a) Our own event log. The app sends a closed list of 77 named events
(such as plan_created, checklist_item_toggled, news_story_opened,
sign_in_completed) to our own servers, with your account id and a per-install
device token attached.
Event parameters are sanitised twice, once on your device and again on our
servers, against a strict pattern that cannot carry free text: no spaces,
no @ signs, non-ASCII dropped. This means a search box's contents, an email
address, an article headline or a URL physically cannot end up in an event.
Search is recorded as "did search: yes/no" and a result count. Target dates are
bucketed into a window rather than sent. Country codes and route identifiers
are sent, so our event log does know which destination you are planning
for.
(b) Google Firebase Crashlytics. When the app crashes, a crash report goes to Google. ⛔ These reports are not anonymous. We attach your account identifier, the opaque UUID that identifies your account in our database. It is not hashed. It means nothing to Google on its own, but it is stable across sessions and across reinstalls of the same account, and we could use it to link a crash to you. We attach it so that when someone tells us the app crashed we can find that crash. It is cleared when you sign out, when you delete your account, and when you turn the consent switch off.
(c) Google Firebase Analytics. Enabled in the same switch. In addition to
our named events, Google's SDK automatically collects things we do not choose,
including first_open and session_start, app and OS version, device model,
platform, a Firebase app-instance identifier, and coarse geographic location
derived from your IP address at Google's end. We do not give Google your
account id for analytics (only for crash reporting), and we do not enable
Google's automatic screen tracking.
(d) Device telemetry. Platform, OS version, device model, app version, language and time zone, keyed to the per-install device token rather than your account. This is what tells us which OS versions we still have to support.
Legal basis: consent (Art. 6(1)(a)), subject to the default-on caveat above.
3.6 Approximate location: optional, and coarse on purpose
If you turn on local news, the app asks your operating system for your position and immediately rounds it to two decimal places (roughly 1.1 km) on your device, before anything else sees it. The rounded value is rounded again before it is sent, and our database column physically cannot store more precision. We use it to lead your news feed with stories about where you are.
- We keep only the latest position. There is no history and no movement log; the row is overwritten in place.
- It is keyed to your install's device token, not your account.
- It is refreshed at most every 15 minutes, and only while the app is in the foreground. Background location is explicitly disabled and we never request "always" permission.
- It is deleted after 90 days automatically, whether or not you do anything.
- Turning it off erases our copy. The app's next telemetry message simply
omits the location, and our server clears the stored coordinate to null. ⚠️
In practice this happens on the app's next launch rather than the same
instant you revoke; see
COUNSEL_REVIEW.md.
One thing that survives. The first time a plan syncs, we derive an Indian state and district (an ISO 3166-2 code, nothing finer) from that coarse coordinate and store it on the plan, so we can see which parts of India our users apply from. ⚠️ That derived region is not erased when you revoke location and is not covered by the 90-day sweep. We are stating this because it is true, not because we think it is ideal; it is on the fix list.
Because it is rounded to about 1.1 km, this is not "precise geolocation" within the meaning of the CCPA/CPRA (which sets the line at 1,850 feet, about 564 m). Under GDPR it is still personal data and is treated as such.
Legal basis: consent.
3.7 Sessions and security
We store a hash of your session token (never the token), when it was created, when it was last used, when it expires, and the first 200 characters of your browser or app user-agent string. This is what lets you see and revoke your own signed-in devices. Legal basis: contract / legitimate interest in account security.
IP addresses. We do not store your IP address in our database and we have no geo-IP lookup. However, our web server writes an ordinary request log that does include the client IP address, and it is used for rate limiting to stop brute-force attacks. Those logs live in our hosting platform's log stream and are transient rather than kept in a database. Legal basis: legitimate interest in security.
3.8 Marketing contact: separate, and opt-in
There is a separate switch, off by default, that says we may contact you about vdaDocs. It is not on the signup screen, it is not bundled with the terms checkbox, and it is not shown at all to users who told us they are under 18.
If and only if you turn it on, your account appears in an internal list that people at vdaDocs can use to contact you. That list shows an operator your email address, your destination country, how ready you are, how many days until your target date and how recently you were active. It does not show your name, your documents, your checklist answers or your location.
That list is gated four ways, and we are describing the gates because they are the actual protection:
- Consent is a hard filter. Rows without a marketing consent timestamp are never returned, on any screen or export.
- Known minors are excluded by the same query.
- Only the account owner role can open or export it; no other staff role holds the permission, and it is rate-limited.
- Every single view and every export is written to an audit log with who looked and when. Caching is deliberately disabled on this screen so that no view can escape the log.
Legal basis: consent. You can withdraw it at any time in Settings, and withdrawal removes you from that list immediately.
3.9 Support look-ups
If you contact us with a problem, a member of staff may look up your account. That surface requires your exact email address or account id (there is no partial search, no wildcard, no browsing) and a written reason, and it writes an audit record naming the staff member and their reason on every look-up, including look-ups that find nothing. It shows account metadata, device and session information, plan counts and event counts. It does not show your documents, your checklist answers, or your coordinate.
3.10 Aggregate statistics
Our internal dashboards are otherwise aggregate-only, and any bucket with fewer than 5 people in it is suppressed rather than shown, so that a statistic can never resolve to a person. The map view buckets locations to half a degree (about 55 km) and applies the same threshold. A separate, clearly labelled synthetic demo dataset exists for screenshots and training; every synthetic row is flagged in the database and is excluded from the real dataset by default.
4. The website
The vdaDocs website is deliberately plain.
- No third-party analytics, no tag manager, no advertising or social pixels, no session replay, no A/B testing service, no CDN-hosted fonts. As of this version the site loads no third-party script of any kind and makes no third-party network request.
- Cookies: when you sign in on the web, we set a single strictly
necessary session cookie. It is
httpOnly(JavaScript on the page cannot read it),securein production, andsameSite=lax. It holds a session reference; your API credentials stay on our server and are never sent to your browser. It is deleted when you sign out or when the session expires. - Because the only cookie is strictly necessary and there are no trackers, the site does not show a cookie consent banner. If we ever add a non-essential cookie or any third-party analytics, we will add a proper consent banner first, and this section will say so.
- If we add a "notify me at launch" email form, we will state at the point of collection what the address will be used for, and it will be used for nothing else.
Reading news articles. The app and site open news stories in an in-app browser pointed at the publisher's own website. When you open a story, that publisher's servers see your request the same way they would in any browser: your IP address, your user agent, and whatever cookies they set. We do not control and are not responsible for what a publisher does; their privacy policy applies to that visit, not ours.
5. Who your data is shared with
We share personal data with three kinds of recipient and no others.
| Recipient | What they get | Why |
|---|---|---|
| Railway (hosting) | Everything, as our infrastructure provider: application servers, the Postgres database, and the request logs containing IP addresses | We have to run the service somewhere |
| Google (Firebase Crashlytics and Firebase Analytics) | Crash reports carrying your account identifier; analytics events; device and app metadata; a Firebase app-instance id; IP-derived coarse geography | Fixing crashes and understanding usage, only if you have not turned the consent switch off |
| Our email provider, once one is chosen | Your email address and the contents of the message, for password-reset emails only | Sending you a password reset |
🔲 Owner decision required: no SMTP provider has been chosen yet. Until one is, password reset emails are not sent; the reset link is written to our server log instead. This must be resolved before public launch, and the provider named in this table. See
COUNSEL_REVIEW.md.
We do not share personal data with advertisers, data brokers, lead-generation firms, immigration consultants, agencies, or anyone else. We may disclose data if we are legally compelled to, and we will tell you if we are permitted to.
News publishers are sources we read from, not recipients; we fetch their public feeds server-side with no user data attached whatsoever.
6. International transfers
Our servers and database are hosted on Railway. Firebase is operated by Google and processes data on Google's infrastructure, which includes servers in the United States.
If you are in the EEA or the UK, this means your data may be processed outside your country. Transfers to Google rely on Google's Standard Contractual Clauses and its supplementary measures, as set out in the Google Cloud/Firebase data processing terms.
🔲 Owner decision required: the Railway deployment region is not recorded anywhere in our code and must be read off the Railway project and stated here explicitly, together with the transfer mechanism relied on for it.
7. How long we keep things
| Data | Retention |
|---|---|
| Account (email, DOB, password hash) | Until you delete your account |
| Plans and checklist ticks | Until you delete the plan or your account |
| Approximate location coordinate | 90 days, swept automatically |
| Other device telemetry (OS, model, app version, locale, time zone) | 180 days, swept automatically |
| News articles | 30 days |
| Session records | Until the session expires ⚠️ |
| Password reset tokens | Until they expire or are used ⚠️ |
| Analytics events | ⚠️ No automatic deletion is currently in place |
| Internal audit records of staff access | ⚠️ No automatic deletion is currently in place |
⚠️ We are flagging the three rows above rather than making a promise we do not keep. The code to expire session records and reset tokens exists but is not currently scheduled to run, and analytics events and audit logs have no retention limit at all. Under DPDP Rule 8 and GDPR Article 5(1)(e) this needs a defined period, implemented, before launch. It is the top item in
COUNSEL_REVIEW.md. Audit records of who accessed whose account arguably should be kept longer than the data they describe; that is a decision for counsel, not a default.
Neither vdaDocs's user numbers nor its category put it within the Third Schedule of the DPDP Rules, 2025 (which imposes a hard three-year cap on e-commerce, social media and online gaming platforms above stated user thresholds). We state the schedule here for completeness, not because it currently binds us.
8. Your rights, and how to actually use them
Depending on where you live, you have rights to access, correct, delete, port and object to the processing of your personal data, to withdraw consent, and to complain to a regulator.
Here is what the product can do today, honestly:
Delete your account: self-service, immediate, and real. Settings → Delete account. This is a hard delete, not a flag: your account row, your sessions, your password reset tokens, and your synced plans and checklist ticks are removed from the database immediately. There is no grace period and no recovery.
⚠️ Two things survive an account deletion, and you should know what they are:
- Your device record: platform, OS version, device model, app version, language, time zone, and any coarse coordinate that has not yet aged out. It is disconnected from your account but not deleted. It ages out on the 90/180 day schedules above.
- Your analytics events: the account id is set to null, but the events remain, still keyed to the per-install device token.
Both of these are pseudonymised rather than erased, and both are on the fix list. If you want them gone as well, contact us and we will do it by hand.
Withdraw consent: in Settings, at any time, for both usage/crash reporting and marketing contact. Withdrawal takes effect immediately and, for location, erases our stored copy.
Access, correction, portability, objection, and complete erasure: ⚠️ there is no self-service export today. These requests are handled by hand. Write to us at the contact address in §1 and we will respond within 30 days.
🔲 Owner decision required: a self-service data export should exist before launch. Handling access requests manually is lawful but does not scale and is fragile.
Complaints. In India, to the Data Protection Board of India. In the EEA or UK, to your national supervisory authority. In California, to the California Privacy Protection Agency or the Attorney General. You can complain to a regulator without going to us first, though we would rather you told us.
9. Children and under-18s
We collect a date of birth from every person who signs up, and we use it to set an internal minor flag. Where that flag is set, we hide the marketing contact option entirely and exclude the account from every contactable list and export.
⛔ This section is incomplete and we are not going to pretend otherwise.
India's Digital Personal Data Protection Act, 2023 treats everyone under 18 as a child and requires verifiable parental consent (with due diligence on the parent's identity and age) before processing a child's personal data, and it prohibits tracking, behavioural monitoring and targeted advertising directed at children. The DPDP Rules, 2025 (notified 14 November 2025) set out how that verification must work, with full compliance required by 13 May 2027.
vdaDocs does not currently implement verifiable parental consent. A person under 18 can sign up today. Excluding them from marketing is a real protection but it is not what the Act asks for.
The owner must choose, before public launch, between (a) blocking signups under 18 outright, and (b) building a verifiable parental consent flow. The wording of this section depends entirely on which. See
COUNSEL_REVIEW.md.
Under GDPR the age of digital consent is 13–16 depending on the member state, which is a lower bar than India's; the Indian standard is the binding one for our primary audience.
10. Security
- Passwords are stored as scrypt hashes and are never stored or logged in plain form.
- Session tokens are stored as SHA-256 hashes; the raw token exists only in your client.
- On the web, your API credentials never reach your browser; they stay on our
server behind an
httpOnlycookie. - Staff access to anything identifying a person is permission-gated by role, rate-limited, and audited.
- Our servers accept a closed allowlist of event names and parameters, which is why free text cannot end up in our analytics even by accident.
⚠️ One limitation we will state rather than hide: on mobile, your session token is stored in the app's ordinary application storage, not in the iOS Keychain or the Android Keystore. It is protected by the operating system's per-app sandbox and by device encryption, but not by the platform's dedicated secret store. This is on the fix list.
No system is perfectly secure. If you believe your account has been accessed without your permission, sign out of all devices in Settings and contact us.
Breach notification. If a breach affects your personal data, we will notify you without undue delay, and will report to the Data Protection Board of India within 72 hours of becoming aware, as the DPDP Rules require, and to EEA/UK supervisory authorities where GDPR applies.
11. Advertising
There is a slot in the news feed reserved for sponsored content. It is currently unsold and renders an empty placeholder labelled "Sponsored". There is no advertising SDK in the app, no advertiser in our database, no ad request, no tracking pixel, and no impression or click event. No data is shared with any advertiser, because there is no advertiser.
If that ever changes, sponsored content will always be labelled as such, and this policy will be updated before any advertising runs, not after.
12. For California residents (CCPA/CPRA)
In the last 12 months we have collected the following categories: identifiers (email address, account id, device token, IP address in server logs), internet and application activity (the event list in §3.5), geolocation data (coarse, as described in §3.6, and not "precise geolocation" as the CPRA defines it), and one category that may count as sensitive personal information depending on how it is read: date of birth, used solely for age determination.
We do not sell personal information and we do not share it for cross-context behavioural advertising, as those terms are defined by the CPRA, and we have not done so in the preceding 12 months. Because we do not sell or share, there is no "Do Not Sell or Share My Personal Information" link.
We use sensitive personal information only for the purposes permitted without a right to limit (performing the service and ensuring security), so no "Limit the Use of My Sensitive Personal Information" mechanism is required. You retain the rights to know, delete, correct and to non-discrimination; see §8.
13. Changes to this policy
This policy carries a version number (see the top of this document). When we change it:
- the version and date change;
- the change is described in the change log below;
- for changes that materially affect you, the app asks you to read and accept the new version on next launch, and we will not quietly re-interpret an old acceptance as covering a new practice.
14. Contact
[CONTACT EMAIL: TO BE COMPLETED]
🔲 Owner decision required. A privacy policy with no working contact address is not a privacy policy. Under the DPDP Rules the contact for answering questions about processing must be published prominently; under GDPR Article 13 it is mandatory. This must be filled in and monitored before launch.
Change log
| Version | Date | Changes |
|---|---|---|
2026-08-26.1 |
2026-08-26 | First draft, written from the source code and database schema. Establishes: mandatory date-of-birth collection at signup; that crash reports carry a stable account identifier and are not anonymous (correcting an earlier statement to the contrary); that Firebase Analytics is enabled alongside Crashlytics; the coarse-location lifecycle including 90-day expiry and the derived region that outlives it; the marketing-contact list and its four gates; the on-device-only reading profile; the absence of any document, passport-number or file handling; and the retention gaps that are not yet closed. Not yet reviewed by counsel. |