Privacy Policy
Last updated
What NthCase keeps about you and the apps you test, where it is kept, who else receives it, and for how long. Every statement on this page describes what the service does today.
Who is responsible
NthCase is operated by Unidots Software Solutions LLC, a Delaware limited liability company. Questions about this policy, and every request it mentions, go to the address below.
Unidots Software Solutions LLC
112 Capitol Trail, Suite A, Newark, DE 19711, USA
support@nthcase.com
The application and its database run on servers in Frankfurt, Germany (European Union), rented from Hostinger. Screenshots and files from your tests are stored with Cloudflare R2, in the European Union. The app builds you upload are stored with Cloudflare R2, in the European Union. Who else receives data, and what leaves the European Union, is set out below.
Your account
When you create an account we keep your e-mail address and the name you give us. If you sign up with a password, we keep only a BCrypt hash of it, never the password itself.
If you sign in with Google or GitHub instead, there is no password: we keep which of the two you used and the account number it gives us. We ask Google for your basic profile and e-mail address, and GitHub for your profile and e-mail addresses. From their answer we keep your e-mail address and your name; from GitHub, the address is only ever the primary one it says you verified. We do not keep a profile picture or the access token they issue.
If you ask to try a plan, we keep the request: when you asked and from which page, the workspace you asked from and the plan it was on, the plan you named and the note you wrote. We keep it to answer you and to improve the product. It also reaches our support mailbox, support@nthcase.com, as an e-mail, so that a person reads it and can reply to you; if many requests arrive in one hour, the later ones are kept but not e-mailed. When we answer it, we keep which answer it was: a code sent to you, a decline sent to you by e-mail, or the request closed without an e-mail. The e-mail that brings you a code, or a decline with any note we add, is kept only until it is sent; after that we keep only which request it answered. The request is kept while your account exists, and deleting your account deletes it; the e-mail stays in the mailbox, as any e-mail you send us does.
If you send us feedback from the app, we keep it: what you said it is, the message you wrote, the workspace you sent it from, the page you were on (its path only, never the rest of the address), your browser and system by name, the size of your window, and when. We keep it to answer you and to improve the product. It also reaches our support mailbox, support@nthcase.com, as an e-mail with your name and e-mail address and your workspace's plan, so that a person reads it and can reply to you; if many messages arrive in one hour, the later ones are kept but not e-mailed. We do not e-mail you about it unless we reply. The message is kept while your account exists, and deleting your account deletes it; the e-mail stays in the mailbox, as any e-mail you send us does.
To check that your e-mail address is yours, we e-mail you a link when you sign up with a password, and again whenever you ask; the link verifies it only when you open it signed in to that account. If you forget your password, we e-mail a link to set a new one to your account's address, whether it is verified or not; if your account signs in with Google or GitHub, we e-mail you that instead. We keep each link only as a hash of its random token, with what it is for, the address it went to, when it was made, when it stops working (a verify link after 3 days, a reset link after 1 hour) and when it was used. The e-mail holds the link only until it is sent. A newer link replaces an older one; a link that stopped working more than 7 days ago is deleted when the next link is made, and deleting your account deletes them all. Signing in with Google or GitHub when they say the address is verified verifies it too; accepting an invitation does not. If that Google or GitHub sign-in is the first to verify it, it also removes the account's password, ends its sessions, the AI tools connected to it and the API keys it made, and disconnects the machines connected to the workspaces it owns, and we e-mail you to say so. Setting a new password with a reset link signs you out everywhere and disconnects the AI tools connected to the account; if the link is the first thing to verify your address, it also ends the API keys the account made and disconnects those machines, and its e-mail says so. If our device farm cannot be reached at that moment, we keep a note of each workspace still to disconnect, with the account and the time, until the farm confirms, and then delete it. Until your address is verified, we send it no notification e-mails, and asking to try a plan, inviting someone and connecting a machine wait for it. When your password changes, we e-mail you to say so. The e-mails a request to try a plan or a message from the app send to our support mailbox say whether your address is verified.
If you sign in with a password, we also keep when you last did so; signing in with Google or GitHub does not record it. We keep whether your e-mail address is verified. Each sign-in session is a random token that we store only as a hash, and a session you do not use for 7 days ends. Your account also keeps one random value that every sign-in carries. Resetting your password, or the first Google or GitHub sign-in that verifies your address, replaces it, so every sign-in from before stops working at once, and a page left open with one stops getting updates. The notifications we show you, and your choices about which ones you receive, are kept with your account. If you close the first-run steps on a workspace's dashboard, we keep when you closed them, so they stay closed wherever you sign in. We also count accounts, workspaces, sign-ins and the work the service does, as totals per day, to run the business; the totals name no one and stay on our own servers.
Google user data
This section covers what we receive from Google when you sign in with Google. We ask Google for three scopes, openid, email and profile: an identifier for your Google account, your e-mail address and your basic profile. From Google's answer we read four things: that identifier, your e-mail address, whether Google has verified it, and your name. We do not read your profile picture or anything else in your profile, and we sign you in only if Google has verified your address. The access token Google gives us is used once, to read those four things, and is not stored.
We keep that identifier, marked as a Google account, so that we recognise you the next time you sign in; your e-mail address; and your name, as the name on your account (your e-mail address stands in when Google gives no name). If you already have an account with the same address, we link your Google account to it and keep the name you gave us; if its address had not been verified, that sign-in also removes its password and ends its other ways in. You can change your name in Settings.
We use this data to sign you in, to match invitations sent to your address, to show who you are and to write to you, and for nothing else. Other members of your workspaces see your name and e-mail address, and your name appears in the workspace's notifications, for example when you join or leave it. If signing up creates a workspace for you, it is named after you. The e-mails we send you go to your address and greet you by name.
Outside the service's own servers, your e-mail address and name go to one company: Hostinger, whose mail service delivers our e-mail, which receives the address a message is sent to and the name it greets. An invitation you send shows your name, and your workspace's name, to the person you invite. None of it is sent to the AI model providers or to the devices your tests run on. We do not sell it, use it for advertising or use it to train AI models, and we pass it to no one else, except as running the service requires or as the law requires.
We keep your Google account's identifier, your e-mail address and your name for as long as your account exists, and the record of each e-mail we send you keeps the address and name it went to for 30 days. The product has no way yet to remove the link to Google or to delete your account yourself: to have your account, that link and those records erased, write to support@nthcase.com. Deleting your account deletes its link to Google. You can also remove NthCase's access to your Google account on your Google Account's permissions page. Because we keep no Google token, removing that access does not erase anything we hold here.
NthCase's use and transfer of information received from Google APIs adheres to the Google API Services User Data Policy, including the Limited Use requirements.
Your workspace and the apps you test
A workspace keeps its name, its members and their roles, and its invitations: the invited e-mail address, the role offered, and the invitation link, stored only as a hash. An invitation sent from an account whose address was not yet verified is marked once that address is verified, and the members page shows the workspace's owner who joined with one.
For each app you register we keep its name and platform, its web address or package name, its settings, the link to its build, and any scope document you attach, including the document's text.
When you start a scope run, we keep the text we read from your scope document to price that run, so the run reads exactly what you were quoted. That copy is kept in the service's own database and never written to its logs. It is deleted as soon as the run reads it. A copy no run reads, because the run was refused or never got to it, is deleted a day after it was kept. Looking at a scope estimate keeps nothing.
Scenarios keep their steps as you wrote them, and the plans compiled from them. Each run keeps its result, a screenshot of each step and a snapshot of what the step's screen held, the picture a visual check makes of what changed, its logs, a capture of its network traffic when its scenario turns capture on, the requests its steps made to an API, and the files it downloaded. Schedules, datasets, the files you upload for a test to send, and the reference screenshots a visual check compares against are kept in the workspace too.
A saved login for your app keeps a label, an optional username and a secret. The secret is encrypted with AES-GCM and has no plain-text copy; it is decrypted only while a test that uses it runs. Logs are scrubbed before they are stored: the values of the run's saved secrets are removed wherever they appear. What a network capture masks, and what it keeps as it was, is set out under network capture.
API keys are stored only as a SHA-256 hash; we show you their first 8 characters so you can tell them apart.
When you confirm that you own an app or are authorized to test it, we record who confirmed, when, which version of the confirmation they agreed to, and the IP address they confirmed from.
The workspace also keeps its plan, its credits and how they were spent, each code it redeemed with who redeemed it and when, and a history of who changed what in it. That history does not record IP addresses.
Network capture
Network capture is off unless a scenario turns it on, with “Capture network traffic” in its device profile. It is offered only on Android: on our own devices, and on a phone plugged into a machine you connect with nth-agent, once someone on that machine has turned it on, as set out below. Not on iOS, not for web apps, and not on a device in your own cloud-lab account. On our Android devices, capture runs when a scenario turns it on, as the rest of this section describes.
On our devices, for the length of the run, the test phone's network traffic goes through a proxy on the machine the phone is attached to, our device node in Malta (European Union): a physical phone reaches the proxy over its USB debugging connection (adb), an emulator directly. To read encrypted (HTTPS) traffic, the phone must trust our capture certificate, in its user certificate store; the node never changes the phone's system certificates. So only an app that trusts user certificates, usually a debug build, can be read: traffic to a host the app pins, and from an SDK with its own network stack, is not recorded. The capture certificate is the device node's own, made for that machine, and its private key stays there: a phone is given only the certificate.
When the run ends, the node hands the phone back: it takes the proxy setting and the adb connection off it, removes the certificate where the capture put it, and reads the phone again to check. On a phone without root access, the certificate is put in the user store once, by hand, with a confirmation on the phone, and stays there until it is removed in the phone's settings; with the proxy setting gone, no traffic goes through our proxy. A copy of the certificate file stays in the phone's temporary folder, where nothing trusts it. The phone is handed back even after the node restarts or loses its connection to us: before it touches the phone, the node writes down what it is about to change, and keeps that record until the phone reads back clean. A phone that still carries any of the capture is not offered to anyone until it reads back clean, or until we have checked it by hand and returned it to service.
On a phone connected through your own machine. Network capture runs there only after someone on that machine turned it on (npx nthcase-agent capture enable, confirmed by typing ENABLE), set up the phone (npx nthcase-agent capture setup), and a scenario asked for it. During such a run the whole phone's traffic, not only the app's, goes through that machine, so use a test phone. The agent masks every request and response in memory on your machine before anything is written or sent, with the same rules we apply again when it arrives. A body the agent cannot read is never sent, in any form: the record says it was left out, and why. That includes binary formats such as protobuf and gRPC, whatever their first bytes look like, and any body with a control character in it. A host that does not accept the certificate (the app pins it, or its library trusts only its own certificates) is recorded once as not readable, and its traffic is then passed through unread. The agent does not connect the phone to your machine itself or to your local network during a capture: such a request is refused, and the record says so, unless the agent was started with --capture-allow-local-network. What reaches us is that masked record, kept with the run for one hour and then deleted. Nothing raw is kept on your machine: the masked copy waits there only until it is collected, and is deleted then, or an hour after the capture stops. The certificate authority the agent makes for this belongs to your machine. The agent never sends its private key anywhere, and keeps the folder that holds it out of your backups; a backup made before that, or by a backup tool that ignores it, may still hold a copy. It lasts one year, and npx nthcase-agent capture disable deletes it. The certificate a person installs on the phone stays there until they remove it in the phone's settings — on a Samsung, in both places it is listed. On a machine other people log into, the machine's administrator (root) could send traffic through the capture proxy while a capture runs; a connection from any other account is refused unread.
For each request, the capture records its method; its full address, with its query string; its status, timing, sizes and content type; the headers of the request and of the answer; and up to 262,144 characters (about 256 KB) of each body. A run keeps at most 500 requests; past that, the earliest are dropped.
Before anything is stored, it is masked: a value is replaced, and its name is kept. The whole value of the Authorization, Proxy-Authorization, Cookie and Set-Cookie headers is replaced. So is the value of any header, JSON field, form field or query parameter whose name says it holds a credential: a password or passcode, a token, a secret, an API key, a private key, a session ID, a signature (also under a name whose last word is sig, such as af_sig), an authentication value under a name whose last word is auth, a cookie, a PIN or one-time code, or a card's security code, however the name is written (accessToken, access_token and Access-Token are one name). In a JSON body this reaches every level, and under a name such as credentials or tokens it reaches everything in the list or object the name holds. Query parameters are masked in every address the capture holds: the request's own, an address in a header such as Location or Referer, and an address inside a body. So are the parameters after an address's #, and its path parameters, such as ;jsessionid=. In an address or a form, the whole value is replaced, even when it holds a quote or a space.
The values of the run's saved secrets are removed as typed; percent-encoded in a form, a query string or the request's address; escaped in a JSON string; and, for a secret of five characters or more, encoded in base64. So are values that look like credentials, whatever their name: bearer and basic credentials, JSON web tokens, private keys, and API keys in the formats of well-known providers.
A body that cannot be read field by field is not stored at all: a binary format such as protobuf, gRPC, MessagePack or CBOR; audio, video, a font, or an image other than an SVG; a body with a control character in it; and any other body that is not text. Its request is still listed, with its address, status and headers, and where the body would be, the run's Network tab says why it was left out.
What is not masked is stored as your app sent it: a credential under a name that does not say so, such as an OAuth authorization code sent as code; a credential written inside a text value rather than as a field of its own, such as an argument in a GraphQL query; the values in an XML or HTML body; the rest of each address, its path included; and the other headers and fields. A body can hold personal data your app sent or received, such as names, e-mail addresses or account details, and it is stored as it was.
The masked capture is stored with the run in the application's database, in Frankfurt, Germany (European Union). The test engine reads the capture file — unmasked from our device node, or already masked from your own machine — into memory, masks it there, and stores only the masked requests: all of them, or, if they cannot be stored, none, and the run says so. When a run has no capture, its Network tab says why. The addresses and ids that would open a device session are left out of the reason our device service gives in its own words, and out of every line the test engine writes into a run's logs. Anyone who can open the workspace can read a capture: every member, whatever their role, the AI tools connected to the workspace, and its API keys, on the run's page and through the API, the nth command line and MCP. The Share evidence download does not include them: it holds a run's pictures.
A capture is deleted an hour after the run that made it. The unmasked file the device node writes, and the node's log of each request's method and address beside it, are deleted from the node 24 hours after the capture stops, by a sweep that runs when the node starts and every half minute after that, so a restart of the node does not keep them. While it captures, the node's console says only when a capture starts and stops; mitmdump's own output, which names each request's address, goes to a file beside the capture and is deleted with it.
Who else receives data
AI model providers. To compile, run and repair tests, we send a model what each task needs: a scenario's name, description and steps; the structure of your app's screen (a web page's source, or a mobile app's accessibility tree); a screenshot, when a step checks something visual or has to pick an element on screen; and the text of a scope document you attach. We use Anthropic's models, and OpenAI's when an OpenAI model is chosen. A saved login's secret is never sent to a model: a model sees only the login's label.
Screenshot and file storage. Screenshots and files from your tests are stored with Cloudflare R2, in the European Union: the screenshots of each step: the screen it acted on, and the screen after it; a snapshot of what each step's screen held (a web page's labels and text, or a mobile app's page source); the picture a visual check makes of what changed; the files your app downloads during a web run; the files you upload for a test to send; and the scope documents you upload. The bucket is private and apart from the one that holds app builds: nothing in it has a public address. The service reads it, and hands your browser a run's pictures and snapshots at addresses that stop working within ten minutes.
App build storage. The app builds you upload are stored with Cloudflare R2, in the European Union. The bucket is private: nothing in it has a public address, and only the service reads it. A build you link instead stays where you host it, and the service fetches it from there when a test needs it. Copies of builds, linked or uploaded, are kept on the application's servers to install them on devices (see how long it is kept).
Hosting. The application and its database run on servers rented from Hostinger, which operates the machines and their disks in Frankfurt, Germany (European Union). It has no account with us; it holds what those servers keep because it sits on its hardware, and its mail service delivers our e-mail.
E-mail. Invitations and notices (a scheduled cycle that failed, a schedule that paused itself, credits that ran low or ran out, removal from a workspace) are sent through Hostinger's mail service, which receives the recipient's address and the message. So is a request to try a plan, which goes to our own support mailbox, and our answer to it, a code or a decline, which comes back to you, and a message you send us from the app, which goes to our own support mailbox. So are the links that verify your address or reset your password, the notice that your password changed, and the notice that a Google or GitHub sign-in closed the other ways into your account.
Google and GitHub, only if you sign in with them.
The devices your tests run on. A test runs on a device in our device farm, in Malta (European Union), or on a machine you connect yourself with nth-agent. The device receives your app's build and every action of the test, including text a step types into your app, a saved login's secret among it, and its screen is streamed back so the run can be watched. When you connect a machine, the farm records the machine's name, its processor cores and memory, and the devices it offers.
A cloud device lab you connect with your own account (see the cloud lab guide) receives your app's build and the test's actions under your own agreement with that lab.
Transfers outside the European Union. What a test sends an AI model, listed above, goes to the model's provider, Anthropic or OpenAI, in the United States, under each provider's data processing terms, which incorporate the EU Standard Contractual Clauses (see Anthropic's and OpenAI's).
Cookies and browser storage
The site sets one cookie, gp_refresh. It holds your sign-in session, cannot be read by scripts on the page, is not sent when another site's page calls this one in the background, and is kept for up to 30 days.
The site also keeps five things in your browser's own storage: your theme, whether the sidebar is collapsed, which workspace you had open, and, only while you sign in with Google or GitHub, the page to return to and a random value that lets the sign-in finish only in the tab that started it.
There is no analytics, advertising or tracking script on the site.
IP addresses
Your IP address is read to limit how often a request can be repeated: signing in, registering, renewing a session, previewing an invitation, opening a live view of a test, and presenting an API key that does not work. These limits hold the address in the server's memory only; they do not write it to the database.
An IP address is stored in two places: with a confirmation that you own an app or may test it, and with an AI tool that registers itself to connect to the service, as the address it registered from.
Your browser loads a run's screenshots, and a step's snapshot when you pick an element on it, straight from Cloudflare R2, at addresses the service signs that stop working within ten minutes; Cloudflare receives your IP address when it sends them.
How long it is kept
Logs captured while a test runs are deleted after 7 days, and logs captured while the service explores your app after 7 days. A network capture is deleted an hour after its run, and the file our device node keeps of it 24 hours after the capture stops. Files a run downloaded are erased after 30 days, and a dataset no run has used for 90 days has its rows erased.
The app builds you upload stay with Cloudflare R2 until they are deleted in one of three ways. When you upload a build to an app, or choose which build it runs, the app keeps up to three: the one it runs and the two newest others. A build no app has taken 24 hours after its upload is deleted. Deleting an app deletes all of its builds. A deleted build's record, with its name, size, app id and version, is kept. The copies of builds, linked or uploaded, that the application's servers keep to install them on devices are pruned each time a copy is added: those older than 14 days go, and so do all but the newest six.
A sign-in session ends after 7 days without use; its cookie is kept for up to 30 days. Server logs are rotated by size: five files of 10 MB per service. They are also kept in a log store on the same server for 7 days, and service measurements (counts and timings, never content) for 15 days; neither leaves our servers. Database backups are kept for 14 days, on Hostinger's servers in Frankfurt, Germany (European Union).
The record of each e-mail we send is deleted 30 days after it is written, once the e-mail has gone or could not be sent. When an AI tool asks to connect to your account, the request is deleted after a day, and a connection's tokens a day after they stop working. An AI tool that registered itself is deleted 30 days after it registered, unless a connection still uses it.
Everything else, screenshots included, is kept while your account and your workspace exist: nothing in the service deletes it on a schedule.
Deleting an app, a scenario or a saved login removes it from the product and marks the stored record as deleted rather than erasing it. Deleting a file you uploaded erases the file and keeps its name, size and dates. To have records erased, write to support@nthcase.com.
What you can do yourself
In the product you can change your password; delete apps, scenarios, schedules, datasets, uploaded files, saved logins and notifications; remove members and revoke invitations, API keys and agent tokens; leave a workspace; and archive one.
Deleting your account, deleting a workspace and exporting your data are not features of the product today. Ask for any of them at support@nthcase.com.
Your rights
Under the GDPR and Turkey's Personal Data Protection Law (KVKK) you can ask to see the personal data we hold about you, to correct it, to have it deleted, to object to how it is used, and to receive it in a portable form. Send the request to support@nthcase.com.
Changes to this policy
When this policy changes, the date at the top of the page changes with it.