What we keep, and what we never see.
Written to be read rather than to be defensible. If something here is unclear, treat the stricter reading as the one we meant.
Your syllabus file
A PDF you upload is opened and read by code running in your own browser. The file itself is never transmitted to us, is never sent to any third party, and is never used to train anything. Closing the tab is enough to be rid of it.
Counting, not tracking
When a schedule is read, we record that one was: whether it came from a PDF, a scan, pasted text or Canvas, how many pages it had, how big the file was, how much text and how many dated lines were in it, how many dates came out, how many of those the parser was unsure of, whether the term came from the document or from its name, how long it took, and whether the person was signed in. If the document could not be opened at all, we also record which of a dozen fixed reasons it was: encrypted, damaged, too large, or a reader that would not load, plus the name of the error's own class. We never record the error message, since that is the one piece of text that can carry a filename or an address, so it is left behind. That row has no account attached to it, no address, and nothing lifted out of the document: not the filename, not a single line of the text. The browser is told not to send your session with it, so it cannot be tied back to you on our end either. It exists so we can tell whether the parser is getting better or worse, which is not something we could otherwise know.
Two things in that row are about your browser rather than your syllabus: its family and major version, as one word ("safari18", never the full identifying string your browser sends to every site anyway), and whether it was a phone, a tablet or a computer. The first is there because the worst bug this app has shipped was invisible without it. Every Safari older than 18.4 failed on every upload for a year, over one missing method, and from our side that looked exactly like a run of bad PDFs; people were told their syllabus was damaged when it was fine. The second is there so a device that is quietly failing more often shows up as that rather than as "people on phones don't finish."
That same row also records what you did next, as five yes-or-no flags: whether any row was
corrected, whether an .ics was downloaded, whether the subscription link was
used, whether the schedule was saved, and whether an account was made during the visit. It
also records one number, how many dates were still on the list when it left. It is the only
way to tell a parse that worked from one that produced thirty rows nobody could use. The
row's identifier is handed back to the page that made it and kept in the tab's memory, not in
a cookie, not in storage, so it is gone when you close the tab, and two visits by the same
person cannot be joined together by us or by anyone else.
If you sign in and save
Saving a schedule stores the list of events (their titles, dates, times, kinds and notes) together with your account. It does not store the source PDF. Each saved schedule also gets a long random subscription token, which is what makes its calendar feed address unguessable.
Your account
An account is an email address, an optional display name and picture if you signed in with Google, your timezone, your reminder preferences, and the date and version of the terms you agreed to when you signed in. There is no password to leak, because we do not use passwords: sign-in is either Google or a one-time link sent to your email.
You can attach a Google identity to an account signed up by email link, from Your account. This is useful if you want the Google button next time even though it's a different address. Attaching never changes the address your account is signed in under, and a Google identity can only ever be attached to one account: whichever one claimed it first keeps it.
Canvas
A Canvas access token you paste is used to make the requests you asked for and is then discarded. It is not written to the database and it is not logged. It travels through our server only because your browser is not permitted to call Canvas directly.
Calendar subscriptions
A subscription address is public in the sense that anyone holding the exact URL can read that schedule's events. That is how calendar subscriptions work everywhere: the protocol has no way to sign in. The token is long and random, it appears nowhere but in your own account, and you can revoke it, which immediately breaks the old address.
We send you three things and no more: the one-time link that signs you in, the reminder digest you turned on, and, rarely, a service notice, which is what an account holder is owed in writing when the terms change or the service is going away. There is no fourth category, no newsletter, and nothing that carries an offer. Every reminder carries an unsubscribe link that works without signing in; a service notice is not a mailing list and the way to stop getting them is to delete your account, which the notice itself tells you how to do.
Reminders are best-effort. They can be late, they can be filtered by your mail provider, and they can quote a date the parser read wrong. They stop entirely if the service does.
When you report a problem
The feedback form sends one email to one person, and that email is the only place your report is kept. It is not stored in the database and it is not on any dashboard. What travels with it is listed in the form itself, under "Exactly what gets sent with this", before you press send: what you wrote, the name and address you chose to give or leave blank, the page you were on, what your browser is, and, when you are reporting a failed import, the file's name and size and what the error was. Nothing from inside your syllabus is in that list.
The syllabus PDF itself goes only if you tick the box that says so, and that box is never ticked for you. It is one of exactly two doors in SyllabusSpy through which "the file never leaves your device" can be set aside. The other is the next section, and both of them are a checkbox you tick yourself. It exists because a PDF our parser cannot read is unfixable from a description of it, and it is yours to refuse: a report without the file still arrives and is still worth sending.
We do record that a report was filed: the day, what it was about, whether a file came with it, and whether there was an address to reply to. That row exists to stop the form being used to send a thousand emails, and it holds a one-way hash of your network address rather than the address, which is enough to count and not enough to identify.
When you send us a syllabus
The review step offers to take the document itself, and it offers hardest when the parser has visibly struggled with it. That offer is the other exception to "the file never leaves your device", and it is a bigger one than a bug report: a contributed syllabus is kept, tested against, and may end up in the sample documents that ship alongside the code.
It is one button, and pressing it is the whole of the decision. The button says what it does before you press it: it sends the PDF as you uploaded it and the text as our parser read it. There is no second confirmation, because a tick box above a button that already says "send it" is the same answer asked for twice.
The dates go with it. Beside the document we send what our parser pulled out of it: each date, what kind of thing it took it for, and the line of the text it read it from, and, when you press the button, the review table as you left it, so we can see what you had to correct. Anything you typed into that table goes too. Both are printed for you under the text, before and after the send. When the switch below sends a syllabus it sends only the parser's own list, because that send happens before there is a table to have changed.
Nothing is removed from it first, and we will not pretend otherwise. A PDF is a layout, and a mark drawn over a name leaves the name in the file underneath; a pattern that hid email addresses and phone numbers would still miss a name written on a line of its own, and calling that "anonymised" would be worth less to you than this sentence is. So do not send a syllabus carrying personal or sensitive information, yours or anybody else's, that you would not want us to have.
The text is printed for you in full either way, and you choose when. Read it first, beside the button, opens it before anything is sent. Otherwise the receipt that appears the moment it goes prints the same text, in the same place on the screen, with a button that deletes the document again. That deletion is good for a day, and it deletes rather than flags. We would rather you read it beforehand, which is why the link is there; we would rather you read it afterwards than not at all, which is why the receipt prints it.
This used to be a form, with a warning panel, the text, two optional fields and a checkbox, and almost nobody finished it, which meant the documents that would fix the parser stayed on people's laptops. Nothing about what we collect changed when that form went: the same file, the same text, still only when you ask for it, still never by default.
The switch that sends all of them
There is one setting that makes that decision in advance, and it is the largest thing you can agree to here, so it is described by what it costs rather than by what it helps with. Turned on, every syllabus you open is sent to us the moment it is parsed (the PDF as you uploaded it, the text as we read it), without asking you again, and before you have looked at what is in it. The one-off button above is still a decision you make with the document in front of you, and it prints the text either side of the send; this switch is you telling us not to stop and ask, which moves the reading to afterwards and moves it to you.
It is off unless you turn it on, it is never turned on by signing in or by anything else you do in the app, and it lives in this browser rather than in an account, so it does not follow you to your phone, and clearing your browser data ends it. Every send it makes is announced on the review screen the moment it happens, and that notice carries two buttons: one that deletes the document again, good for a day, and one that switches the whole thing off. Switching it off stops the next one; it does not recall the ones already sent, which is what the delete button is for.
What we agree to in return is that the wording you turned it on under is the wording it keeps working under. The stored setting carries a version stamp, and if what this switch means ever changes, every copy of it stops sending until its owner has read the new wording and said yes again.
A syllabus we already have is not stored twice. Your browser sends a fingerprint of the document (a SHA-256 hash, which says nothing about what is inside it) so that re-uploading the same file after correcting a row does not put five copies in the archive.
Contributed documents are kept in private storage, numbered, and are not deleted on a schedule, because a document given away to fix the parser is no use to anyone if it is thrown away in a year. That is the point of the delete button, and of writing all of this down before you decide.
What the database records is a tally and nothing more: the day, how the document was read, how many pages and events came out of it, how many the parser was unsure of, how big the file was, whether it came from the switch or from a one-off yes, and whether there is an address to thank. Not the text, and not the filename. The filename travels attached to the document itself, because our parser reads it for the term when a syllabus names one nowhere else.
What we do not do
No advertising. No third-party analytics, no tracking pixels, no cookies that follow you anywhere. No selling or sharing of anything with anyone. No profile built from your coursework: the counts above are totals, and there is nothing in them to build one from.
Deleting things
Deleting a schedule removes its events and its subscription token. Deleting your account removes the account, every schedule under it, and every reminder record. Both take effect immediately, and neither leaves a copy behind.