Agents produce files: screenshots, test reports, build outputs, generated code. developerz.ai stores these as artifacts, objects in an S3-compatible bucket with presigned URLs for upload and download. Bucket credentials stay server-side; you only ever see short-lived presigned URLs.
Storage options
| Option | Setup | Default expiry | Max expiry |
|---|---|---|---|
| Platform bucket | Zero config. Available on all tiers. | 7 days | 7 days (fixed) |
| BYO S3 | Connect any S3-compatible bucket in Settings → Storage. | 7 days | "never", or any TTL up to 10 years |
To lift the 7-day cap or keep artifacts longer than a week, connect your own bucket.
The artifact.put MCP verb surfaces expiry_capped: true (with an explanatory note)
if you request a longer expiry on the platform bucket.
Connecting a BYO bucket
- Go to Settings → Storage in the dashboard.
- Enter your bucket name, endpoint, region, access key, and secret.
- Click Test connection, the platform writes and reads a sentinel object.
- Save.
Supported: Amazon S3, Cloudflare R2, Backblaze B2, MinIO, and any
s3:// endpoint that implements PutObject, GetObject, HeadObject,
and DeleteObject.
Uploading an artifact
# MCP, register the artifact and get a presigned PUT url
artifact put \
--content-type "image/png" \
--filename "screenshot.png" \
--size 204800 \
--expires-in 604800
# → { id: "art_…", upload_url: "https://…", upload_expires_in_seconds: 3600 }
# Upload the bytes (from the agent or your machine)
curl -X PUT -H "Content-Type: image/png" \
--data-binary @screenshot.png \
"<upload_url>"
The upload_url is a presigned PUT that expires in 1 hour, the same window a
download URL gets. Upload before then; the artifact row is created immediately (so
artifact list sees it even if bytes haven't landed yet, with has_bytes: false).
Downloading / sharing
# Get a presigned download URL
artifact get --artifact-id art_…
# → { url: "https://…", expires_in_seconds: 3600 }
# Mint a fresh URL for an existing artifact (e.g. to share with a user)
artifact share --artifact-id art_…
Presigned download URLs expire in 1 hour by default. Refresh with artifact share.
Expiry model
| Field | Accepted values | Default |
|---|---|---|
expires_in |
a whole number of seconds (3600, 604800, …), or the literal "never" |
604800, 7 days |
Seconds, not a duration string: "7d" is a validation error, not seven days. The
ceiling on a number is 10 years; "never" means the artifact lives until you
delete it, and needs a BYO bucket (the platform bucket caps everything at 7 days).
Expiry is from artifact creation, not first access. A reaper janitor sweeps hourly
and deletes the stored object and its metadata row together, so neither outlives
the other. There is no grace window and nothing is soft-deleted: once expires_at has
passed, the next pass removes both. A "never" artifact is never touched.
Listing artifacts
# All artifacts for the account
artifact list
# Narrow to one task
artifact list --task-id <task_id>
# Narrow to one fleet run
artifact list --run-id <run_id>
# Narrow to one repo
artifact list --repo-id <repo_id>
Visibility
Artifacts are always account-scoped, only the account that created them can access or share them. There is no public artifact URL; sharing always requires a presigned URL from an authenticated call.
Audit trail
Every artifact operation (put, get, share, delete) is written to the
append-only audit log with the token ID, timestamp, and artifact ID.
Full verb reference: MCP verb reference → artifact.