Documentation · macOS 15+
Agent & script reference
filecrow is a macOS 15+ command-line sender using the same delivery API,
ZIP64 STORED archive, multipart transport, CRC32/SHA-256 entry metadata, and
server size/ready checks as the Mac app. It ships inside the Filecrow Mac app.
It uploads one archive; it never uploads originals separately. Its credential
and recovery directories belong to the CLI.
Quick start
Filecrow sends files as one private download link. To send files for a person,
run this one command, replacing PATH with what they asked to send. It finds
the CLI inside the Filecrow Mac app and sends:
F="$(command -v filecrow || true)"
for A in /Applications/filecrow.app "$HOME/Applications/filecrow.app" \
"$(mdfind "kMDItemCFBundleIdentifier == 'com.clientdelivery.operator'" 2>/dev/null | head -1)"; do
[ -n "$F" ] && break
[ -x "$A/Contents/Helpers/filecrow" ] && F="$A/Contents/Helpers/filecrow"
done
[ -n "$F" ] || { echo "filecrow not found" >&2; exit 127; }
"$F" send --json --no-interactive -- PATHWhat to send.
- A folder: pass the folder. Everything inside it is sent, subfolders
included, with its structure kept. Hidden files and folders (names starting
with
.) and empty folders are skipped automatically, and symbolic links are refused, so there is no need to list files to leave those out. The delivery is named after the folder. When you explain the command to the person or to a command reviewer, say that Filecrow skips hidden files. - Specific files: list them, and add
--title 'NAME'; otherwise the delivery is named after the first file. - Large sends take as long as the upload takes; run the command in the background if your tool has a short time limit.
Reading the result. Stdout is one JSON object.
- Exit
0,"ok": trueand"data.status": "ready": give the persondata.public_urlexactly as printed, as the full URL in plain text rather than behind link text: they usually paste it to someone else. filecrow not found(exit127): follow If Filecrow is not installed.error.codeauthentication_required(exit3): the CLI is not signed in on this Mac yet. Follow Authentication and confirmation, then run the same command again.error.codecredential_unavailable(exit3): your own command sandbox is blocking the macOS Keychain; this is not a missing sign-in, so do not start a login. Run the command outside the sandbox. See Agent sandboxes.- Anything else: give the person
error.message, do not claim the files were sent, and see Handling failures.
Never run Contents/MacOS/filecrow: that is the app itself, not the CLI. Use
--json --no-interactive on every command you run; the rest of this page is
reference.
Agent sandboxes
filecrow needs three things a coding agent's sandbox often blocks: the
network (to filecrow.com and the upload storage), the macOS Keychain (where its
sign-in is saved), and write access to
~/Library/Application Support/FilecrowCLI (its resumable-upload state).
Blocked Keychain access shows up as credential_unavailable; blocked network
as network_error (exit 5). If your tool runs commands in a sandbox, request
to run filecrow outside it from the start. Reading these docs needs no
browser; an ordinary HTTP fetch of this page is enough.
Installation
The CLI is included in the Filecrow Mac app, version 0.3.7 and later. There is nothing separate to download, and it updates with the app. It requires macOS 15 or later and lives at:
/Applications/filecrow.app/Contents/Helpers/filecrowIf the app was moved, the path is the app's location followed by
Contents/Helpers/filecrow. Contents/MacOS/filecrow is the app itself, not
the CLI. There is no separate CLI download or Homebrew formula; do not use a
guessed brew command or download URL.
If Filecrow is not installed
Filecrow is invitation-only, so installing it needs a person: their invitation, their account and their email confirmation. An agent must not download or install it on its own initiative. Tell the person:
Filecrow isn't installed on this Mac. Open https://filecrow.com/install, download the app, drag filecrow into Applications, open it and sign in. Tell me when that's done and I'll send the files.
If they want you to start the download for them, ask first, then open
https://filecrow.com/install in their browser and let them finish. If they
have no Filecrow account, say so and point them to https://filecrow.com/;
there is nothing more you can do.
If the app is older than 0.3.7
The app is installed but has no Contents/Helpers/filecrow. Ask the person to
open filecrow, choose filecrow › Check for Updates… in the menu bar, and
click Install Update when it appears. The CLI is present once the app has
relaunched.
Commands
filecrow login [--email ADDRESS]
filecrow logout
filecrow whoami
filecrow send <path...> [--title TEXT] [--from TEXT] [--expires DAYS]
[--password | --password-file PATH] [--new]
filecrow send --resume ID
filecrow deliveries [--limit 1..100] [--cursor CURSOR]
filecrow delivery ID
filecrow delete ID [--yes]| Command | Purpose |
|---|---|
login |
Sign in through emailed browser confirmation |
logout |
Revoke this CLI session and remove its credential |
whoami |
Show the current account and allowance |
send |
Send one ZIP64 STORED delivery or resume a draft |
deliveries |
List deliveries with cursor pagination |
delivery |
Inspect a delivery and its file manifest |
delete |
Delete a delivery and disable its recipient link |
All commands accept --json, --quiet, and --no-interactive. --json implies
noninteractive operation. Use --help on any command for its options.
Authentication and confirmation
filecrow login --email ADDRESS requests a magic-link email and waits for
approval until the server challenge deadline. Open the email's link and confirm
sign-in in a browser; approval can happen on another computer. --no-interactive
disables terminal prompts, but it does not bypass email approval. A login with
--email --json --no-interactive can still wait for that human action.
Credentials use the CLI's own macOS Keychain item, separate from the Mac app's,
so signing in to the app does not sign in the CLI. Each Mac needs one CLI sign-in;
after that it stays signed in until logout or the session expires.
An agent does not need to check the sign-in before sending: send itself
reports authentication_required (exit 3) when the CLI is not signed in.
whoami --json --no-interactive shows the account when that is what you want
to know. When a command reports authentication_required:
- Ask the person which email address their Filecrow account uses, and tell them: "A Filecrow email is on its way. Open it and click Confirm."
- Run
filecrow login --email ADDRESS --json --no-interactive. It waits until they confirm, for up to 15 minutes; run it in the background if your tool has a shorter time limit. - Run the original command again. If macOS showed a Keychain dialog, the person should click Always Allow.
If you cannot run a waiting command at all, ask the person to run
filecrow login --email ADDRESS in Terminal instead. Never request an account
token, reuse the Mac app's credential, or repeatedly retry an unauthenticated
send. A refused sign-in may also require account access; report the returned
error to the person.
delete prompts for y in an interactive terminal. In JSON or noninteractive
mode it requires --yes; without confirmation it returns confirmation_required
and exit 9. Inspect the intended delivery and obtain the human's authorization
before an agent runs delete ID --yes. No active upload is deleted on interruption.
logout revokes the CLI session server-side before removing it locally. If the
network cannot acknowledge revocation, the credential is retained and the command
fails so logout can be retried.
File selection
Directories contribute their contents without their top directory. Individual
files contribute their basename; multiple roots merge into the same archive.
Paths are NFC normalized and sorted deterministically. Duplicate archive paths,
symlinks, special files, unreadable files, and invalid paths are refused. Dotfiles
and dot directories inside a traversed directory are omitted; an explicitly
selected hidden regular file is included. Empty directories are omitted. Use
-- before filenames beginning with a dash.
Output for scripts and agents
Stdout carries one final result. Progress and human diagnostics use stderr. Animation appears only on a terminal, never in JSON or quiet mode. Progress counts bytes acknowledged by the server and uses the shared network speed tracker; source reads do not become confirmed upload progress.
With --json --no-interactive, stdout contains exactly one newline-terminated
JSON object on success or failure. Parse stdout and check the process exit status
and ok; do not parse human text or progress. send --quiet prints only the URL
on success. Other human commands print a readable result. Help and version are
plain text even with --json.
If a process is killed or stdout breaks, a complete envelope may be absent. Treat missing/truncated JSON as a failed command and inspect or resume the transfer; never infer successful delivery from partial output.
Success envelope (formatted here for readability):
{
"schema_version": 1,
"command": "send",
"ok": true,
"data": {
"delivery_id": "11111111-1111-4111-8111-111111111111",
"status": "ready",
"public_url": "https://files.filecrow.com/d/<recipient-token>",
"file_count": 147,
"source_bytes": 18400000000,
"archive_bytes": 18400025000,
"expires_at": "2026-10-09T00:00:00Z",
"resumed": false
}
}Error envelope (missing authentication example):
{
"schema_version": 1,
"command": "send",
"ok": false,
"error": {
"code": "authentication_required",
"message": "Run filecrow login first.",
"retryable": false,
"resumable": false
}
}Failures use the same envelope with ok:false and error instead of data.
An error contains code, message, retryable, and resumable. Where available,
it adds delivery_id, resume_deadline, backend_code, request_id, and
details. No raw session token or password is returned. IDs are UUIDs. Byte
counts are integers; send expiry/deadline strings are UTC ISO 8601. Account and
history/detail timestamps preserve the API's Unix seconds. Unknown additive
fields must be ignored by consumers. schema_version changes for breaking output
changes. The example recipient token above is a placeholder, not a live link.
Stable exit codes
| Exit | Meaning |
|---|---|
| 0 | Success |
| 1 | Internal/unclassified API failure |
| 2 | Arguments or selection |
| 3 | Authentication or credential storage |
| 4 | Entitlement, quota, expiry ceiling, or sending disabled |
| 5 | Network failure or uncertain creation |
| 6 | Changed source, ambiguous/conflicting resume, expired draft |
| 7 | Recovery state or competing process |
| 8 | Archive verification failure |
| 9 | Destructive confirmation missing |
| 130 / 143 | SIGINT / SIGTERM |
Recovery
Rerun the same send ./folder after a crash or a network failure. A single matching
unfinished CLI transfer resumes automatically. The complete file plan must still
match: path, size, and modification time. A partially sent entry is re-appended
whole through the existing archive checkpoint machinery. No partial-file hash
state or alternate journal format is introduced.
Use send --resume ID when several transfers match. Resume uses saved paths and
delivery options; it accepts no new selection/options. Omit password options when
resuming; the original password is already set on the delivery. --new deliberately starts
a new delivery and preserves prior drafts. Use delete ID --yes to delete a draft
and its local recovery state after server acknowledgement. Keep the resume
deadline in error output: the multipart upload cannot be continued after it lapses.
A missing/truncated spool or changed source fails closed.
Creation is not automatically retried. A lost creation response leaves a marker
and returns creation_outcome_unknown; inspect deliveries before using --new.
This avoids silently creating more charged deliveries. It does not add server
creation idempotency.
After sealing, CLI metadata preserves the manifest, expected size, and recipient URL until commit/complete/ready and output succeed. Recovery reuses those existing idempotent endpoints. If the server is already ready it returns the original URL. An output failure retains the result for a later retry. The existing Mac app's checkpoint cleanup remains unchanged.
Configuration and security
The default API/account/auth origin is https://filecrow.com. Recipient links
come from the server, normally https://files.filecrow.com; the CLI never creates
one from the API origin. FILECROW_API_ORIGIN can select another HTTPS origin
(loopback HTTP is allowed for development). Credentials and state are namespaced
by that origin.
FILECROW_STATE_DIR overrides the default
~/Library/Application Support/FilecrowCLI. Recovery directories are private
(0700); CLI metadata and locks are 0600. Do not share or commit them: they contain
source paths and recipient capabilities. Passwords and sessions are not stored
there. The Keychain service is com.filecrow.cli; it never borrows the app's
session. Only an intentional interactive login allows credential UI. Unattended
credential access fails promptly if permission/unlocking is required.
From and expiry default to the account's settings. No configuration file or production environment-token fallback is provided. An agent uses an already signed-in CLI without receiving an account secret.
Task recipes
Use --json --no-interactive for every automated operation. These recipes assume
a signed-in CLI and write it as filecrow; substitute the full path from
Quick start if it is not on PATH. JSON extraction recipes also
require jq. Use the
recipient URL returned by send, never construct one from the API origin. A
success requires exit 0, ok: true, and data.status: "ready".
Send this file or folder and return data.public_url
Replace the path with the requested file or folder. Quote it, and use -- for
paths beginning with a dash. This recipe preserves a failing CLI exit code and
prints only the verified recipient URL on success:
if result=$(filecrow send --json --no-interactive -- ./folder); then
printf '%s\n' "$result" | jq -er '
select(.schema_version == 1 and .ok == true and .data.status == "ready")
| .data.public_url'
else
status=$?
printf '%s\n' "$result" >&2
exit "$status"
fiFor one file, replace ./folder with ./final.pdf. Preserve the error JSON when
reporting a failure to the human; do not claim the transfer completed.
Send with delivery options
filecrow send ./folder --title 'Client finals' --from 'Studio' --expires 7 \
--json --no-interactiveUse --password-file /path/to/private-password.txt if a password is requested.
The file must be private (chmod 600); do not put the password in command arguments.
Check account and inspect deliveries
filecrow whoami --json --no-interactive
filecrow deliveries --limit 20 --json --no-interactive
filecrow delivery "$DELIVERY_ID" --json --no-interactiveFor the next history page, pass the returned data.next_cursor to
deliveries --cursor "$CURSOR". A null cursor means there is no next page.
Resume an interrupted send
filecrow send --resume "$DELIVERY_ID" --json --no-interactiveUse the error.delivery_id from the failed send. Alternatively, rerun the same
original selection for automatic resume. Do not use --new as a retry strategy;
it deliberately creates another delivery. Do not supply new password/options
with --resume.
Delete an authorized delivery
filecrow delivery "$DELIVERY_ID" --json --no-interactive
# After explicit authorization for this delivery:
filecrow delete "$DELIVERY_ID" --yes --json --no-interactiveHandling failures
On authentication errors (exit 3), request human sign-in as described above.
On quota or entitlement errors (exit 4), report the limitation; retrying does
not grant capacity. For a resumable network failure, retry the same selection or
saved ID with bounded attempts. Preserve error.resume_deadline when reporting
unfinished work. Source changes, missing recovery state, or an expired deadline
require human review; do not silently discard the draft or create a replacement.
For creation_outcome_unknown, inspect deliveries and ask the human to resolve
the uncertain creation before using --new. Never report data.public_url from
a failed envelope, and never treat a draft's URL as a completed send.