Short version: your photos and the words on your slides never leave your device except to your own account, and nothing at all is counted unless you say yes.
If you sign in, your carousels and the photos in them are saved to your account so you can pick up a post on another device. The photos are stored exactly as they came off your camera, in a private bucket that only your account can read. Nobody else can see them, and they are not used for anything but showing them back to you. If you do not sign in, everything stays on the device you made it on.
Signing in with Google tells us your email address, and — if Google provides them — your name and profile picture, which is what puts your name at the top of the settings panel. That is the whole of it. None of it is passed to anyone.
Software gets better when the people making it can see which parts are slow, which parts break, and which parts nobody can find. So the app can count what happens as you use it — but only if you agree, and it asks once, in plain words, after you have made your first carousel. Until you agree, nothing is collected, nothing is queued, and nothing is stored.
A random identifier for your device, made up on your device and meaning nothing anywhere else; whether you were signed in, but never which account; roughly where you are, from your device's timezone and the language it is set to; whether this is the website or the iOS app; the version of the app; the rough size of your screen; and a three-way guess at how fast your device is, so slow operations can be attributed to old hardware rather than to bad code.
Alongside that, one row for each of the following things happening. This list is generated from the app's source code, so it is complete by construction:
| Event | What it means |
|---|---|
app_open | The app was opened, and whether it was a fresh start. Carries: cold (yes or no) |
screen_view | Which of the app’s screens was shown. Carries: screen (one of: home, editor, settings, export, shared, insights) |
carousel_new | A carousel was started, from a template, blank, or from photos. Carries: source (one of: template, blank, photos); ratio (one of: 4:5, 1:1, 9:16, 1.91:1) |
photos_imported | How many photos were brought in, and how long it took. Carries: count (a number); heic (a number); ms (a number) |
slide_added | A slide was added to a carousel. |
text_added | A text layer was added to a slide. |
format_applied_everywhere | One slide’s style was pushed across the whole carousel. Carries: slides (a number) |
export_started | An export began. Carries: format (one of: jpeg, png, zip); res (one of: hi, lo); slides (a number) |
export_finished | An export completed, and how long it took. Carries: format (one of: jpeg, png, zip); res (one of: hi, lo); slides (a number); ms (a number) |
share_link_created | A share link was made for a carousel. |
sign_in_started | A sign-in was begun, and by which method. Carries: provider (one of: email, google) |
sign_in_completed | A sign-in succeeded, and by which method. Carries: provider (one of: email, google) |
friction | The app refused something, or hit one of its own limits. Carries: key (one of: upload_incomplete, slides_capped_on_import, slides_capped_on_add, base_photo_undeletable, slide_photos_full, slide_photos_capped, span_needs_slides, span_photo_too_small, paint_wrong_layer_kind, sync_unreachable) |
error | Something threw: the kind of error, which part of the app it came from, and a fingerprint of the message. The message itself is never sent. Carries: type (one of: Error, TypeError, RangeError, SyntaxError, ReferenceError, DOMException, QuotaExceededError, AbortError, NetworkError, Unknown); where (one of: render, import, export, sync, auth, share, fonts, storage, unhandled); fingerprint (a number); repeat (yes or no) |
sync_failed | A sync with the account did not complete. Carries: where (one of: push, pull, photos) |
export_failed | An export did not complete. Carries: format (one of: jpeg, png, zip); slides (a number) |
import_rejected | A chosen file could not be imported, and why. Carries: reason (one of: unsupported_type, too_large, decode_failed, too_many); count (a number) |
perf | How long something took, in milliseconds. Carries: metric (one of: cold_start, export_total, export_per_slide, import_total, import_per_photo, heic_decode, sync_round_trip); ms (a number); n (a number) |
Ninety days, then it is deleted automatically. Google Analytics keeps its own copy for as long as its retention setting allows, which is two months by default.
If you say yes, the app also loads Google Analytics, which counts visits and pages. Like any website request it sees your IP address, and it sets its own cookies to recognise a returning visitor. Its advertising features — Google signals and ad personalisation — are switched off, so it is not used to show you ads. If you say no, it is never loaded. Nothing is sold, and there is no advertising network. The app's own identifier means nothing outside this app's own database. The data is stored with Supabase, our database provider.
Open Settings in the app. There you can:
You do not have to ask anyone to do any of this, and you do not have to explain why.
Depending on where you live, you may have the right to see what is held about you, to correct it, to have it deleted, and to object to it being collected at all. The three buttons above are those rights, as buttons.