On this page
Credentials and files
Two things you give the platform so a step can use them: a login for your app, and a document to send into it. Both travel one way. No read returns a secret you stored, and nothing returns a file you uploaded, so this page says what you will not get back as plainly as what you can do.
Credentials
A credential belongs to one app: a label, an optional username and a secret. You keep them in the app's Credentials, opened from the apps list or from the app's own page, where the product calls them saved logins.
The secret is write-only. You type it once. No read of any kind returns it, because the answer has no field to carry it: a stored credential reads as configured, never as a value, and the secret box is empty every time you open it. To rotate, type a new secret and it replaces the stored one. Leave the box empty and the stored secret stays as it was.
A step names a credential by its label. {{secret:Example Admin.password}} sends the secret and {{secret:Example Admin.username}} sends the username. A label may hold spaces, but not braces. The step carries the reference, never the value.
One credential can be the app's default login, and discovery signs in with it. Marking another one as the default clears the first, so an app has one at most.
Basic auth takes its password the same way. When your app's browser settings send an HTTP basic-auth login, you pick one of the app's saved logins, and the settings hold a reference to it, never the password as text.
The code in this app that adds, edits and deletes credentials takes no logger at all, on purpose: there is nowhere in it that a secret could be written to.
Where a secret is decrypted
A run decrypts every secret its steps reference once, when it starts, and holds the values for that run only. Discovery and a recording session decrypt a secret when they use it. Nothing else decrypts one. The part of the service that answers requests encrypts a secret you set, with AES-GCM and no plaintext column, and never reads one back. This app stores no secret, no ciphertext, no key and no hash: only the label, the username, whether it is the default login, and the fact that a secret is set.
What a run keeps is scrubbed of what it typed. Every secret a run decrypted is replaced with a mask in its logs, and in its network capture's addresses, headers and bodies, before either is stored, and a step's notes never carry one. A capture also masks the values of fields and query parameters named like credentials; the privacy policy says what it masks and what it keeps as your app sent it. Screenshots are pictures of the screen and are not masked: a password field shows whatever your app shows in it.
Your files
Files are documents your workspace gives the platform so a scenario can send them into your app: a CV for a job form, a signed contract for an onboarding flow. You upload them on the Files screen, one at a time. Each is listed with its name, its size, the type the upload declared, when it arrived, its reference, and the scenarios whose steps already name it.
A step sends a file by its reference, {{file:Example CV.pdf}}, written as the whole value of an upload step. The name inside the braces is the file's name, character for character, and a compile refuses a reference that the scenario's own steps never wrote.
A step sends a file into a web app only. On a phone, choosing a file happens in the phone's own file picker, outside your app, so a phone step that asks to attach one is not compiled: the compile stops and names that step.
There is a ceiling on size, and on how many files a workspace keeps. It is the AI Testing service's, and this app reads it from the service every time instead of keeping a copy, which is why this page prints no number. The Files screen states it, and so does the REST API's limits route.
A file stays until you delete it. No timer erases one, because a timer would break a scenario that still names it. What a run sent is on that run's page: the file's name, size and type, a hash of the bytes that went, the page they went to, and whether they were yours. The bytes themselves are not there.
Deleting a file
Before you delete a file, the Files screen tells you which scenarios still name it and what their next run will read. The bytes are erased first, and only then does the file's row become a record of what it was: its name, size, type, a hash, and the two dates.
A run that reaches a step naming a deleted file stops at that step, with a sentence saying which file, how big, when it was uploaded, when it was deleted, and that nothing was sent. It never says there is no such file, because there was one.
Your files are not a run's files
A run can keep files too: the downloads your app hands the browser during a web run. They sit on a different shelf, and they do come back.
| Shelf | What it holds, and whether it comes back |
|---|---|
| Your files | What your workspace uploaded, listed on the Files screen. Never handed back: no download and no preview. Each is kept until you delete it. |
| A run's kept files | What your app handed the browser during that run, listed on the run's page. Handed back: each kept file is a download until the retention sweep erases its bytes, and its record stays. A phone run keeps none. |
The shelves meet in one place. An upload step can send from either: a bare name means a file this run downloaded, and {{file:Example CV.pdf}} means yours. The reference is how the author says which, so a run never has to guess.
From CI, the CLI and the MCP server
Credentials are set in the app, and only there. Their routes do not accept a workspace key, and neither the nth command line nor the MCP server has a command for them. A person types a secret, into a box that never shows it again.
Files take a workspace key. Every files route accepts one, because CI is what puts a file in place before a run. The routes are in the REST API's Files section. The command line and the MCP server have no file command. A run's kept file can be fetched with the same key, because CI is what downloads a report to check it.