Summary
On Basecamp 5 accounts, basecamp attachments download <id> --type comment (and <recording> show --download-attachments=<dir>) saves the preview rendition instead of the original blob. Same on the SDK layer for any integration that uses richtext.ExtractAttachments / the SDK's URL rewriter.
This is a regression of #290 / PR #360 caused by Basecamp 5's storage host change.
Reproduction
CLI: basecamp version 0.7.2 (latest).
Account: any Basecamp 5 account with an inline image attachment ≥ 1500×1875 px on a comment.
basecamp attachments download <comment-id> --type comment --account <id> --project <id> --out /tmp/x
sips -g pixelWidth -g pixelHeight /tmp/x/*.png
Expected: files at upload dimensions (e.g. 1500×1875 for a 4:5 static).
Actual: files at preview dimensions (e.g. 480×600), nondeterministically variable across runs (one run yields 338×600, next yields 1080×1920 for the same blob — preview server returns variable-size renditions).
Root cause
The <bc-attachment> element in Basecamp 5 rich text contains both URLs:
<bc-attachment
sgid="..."
content-type="image/png"
url="https://preview.app.basecamp.com/<acct>/blobs/<uuid>/previews/full"
href="https://storage.app.basecamp.com/<acct>/blobs/<uuid>/download/<filename>"
filename="image.png"
filesize="1974425"
width="1500" height="1875">
The CLI's COMMUNIQUE (COMMUNIQUE-inline-attachments-api.md) documents the intended flow:
"The href values in <bc-attachment> tags are storage URLs (https://storage.3.basecamp.com/...). The SDK rewrites these through the API host for auth, then follows a redirect to a signed S3 URL."
The SDK's URL rewrite table handles storage.3.basecamp.com (BC3-era host) but not Basecamp 5's storage.app.basecamp.com. When the rewrite fails to recognize the storage host, the downloader falls back to the url attribute (preview) instead of href (original).
Empirical confirmation
After manually rewriting storage.app.basecamp.com → 3.basecampapi.com (the API host the SDK would have rewritten to), Bearer auth works:
TOKEN=$(basecamp auth token --account <acct>)
curl -sL -H "Authorization: Bearer $TOKEN" \
"https://3.basecampapi.com/<acct>/blobs/<uuid>/download/<filename>" \
-o /tmp/orig.png -w "%{http_code} %{content_type} %{size_download}\n"
# → 200 image/png 1974425 (exactly matches bc-attachment filesize)
sips -g pixelWidth -g pixelHeight /tmp/orig.png
# → 1500 / 1875 (exactly matches bc-attachment width/height)
So the originals are reachable via Bearer auth on the API host — the only missing piece is the SDK's URL rewriter recognizing the new storage host.
Proposed fix
Add storage.app.basecamp.com to the SDK's URL rewrite table alongside storage.3.basecamp.com. Likely in basecamp-sdk somewhere near the existing host mapping. Two-line change.
If preview-vs-original is also a design intent we want to expose, an --original / --rendition <full|preview> flag on attachments download would be a clean follow-up — but the immediate regression fix is just the host map.
Workaround for downstream users
Until the SDK ships the fix, a small wrapper that does the URL rewrite + Bearer curl works around the issue. Happy to share the script if helpful.
Environment
basecamp version 0.7.2 (from curl -fsSL https://basecamp.com/install-cli | bash, March 2026)
- macOS 14 (Sonoma); same behavior reported by other developers on a separate platform (see n8n community thread linking storage-CDN auth as known limitation)
- BC5 account on
app.basecamp.com
References
Summary
On Basecamp 5 accounts,
basecamp attachments download <id> --type comment(and<recording> show --download-attachments=<dir>) saves the preview rendition instead of the original blob. Same on the SDK layer for any integration that usesrichtext.ExtractAttachments/ the SDK's URL rewriter.This is a regression of #290 / PR #360 caused by Basecamp 5's storage host change.
Reproduction
CLI:
basecamp version 0.7.2(latest).Account: any Basecamp 5 account with an inline image attachment ≥ 1500×1875 px on a comment.
Expected: files at upload dimensions (e.g. 1500×1875 for a 4:5 static).
Actual: files at preview dimensions (e.g. 480×600), nondeterministically variable across runs (one run yields 338×600, next yields 1080×1920 for the same blob — preview server returns variable-size renditions).
Root cause
The
<bc-attachment>element in Basecamp 5 rich text contains both URLs:The CLI's COMMUNIQUE (
COMMUNIQUE-inline-attachments-api.md) documents the intended flow:The SDK's URL rewrite table handles
storage.3.basecamp.com(BC3-era host) but not Basecamp 5'sstorage.app.basecamp.com. When the rewrite fails to recognize the storage host, the downloader falls back to theurlattribute (preview) instead ofhref(original).Empirical confirmation
After manually rewriting
storage.app.basecamp.com→3.basecampapi.com(the API host the SDK would have rewritten to), Bearer auth works:So the originals are reachable via Bearer auth on the API host — the only missing piece is the SDK's URL rewriter recognizing the new storage host.
Proposed fix
Add
storage.app.basecamp.comto the SDK's URL rewrite table alongsidestorage.3.basecamp.com. Likely inbasecamp-sdksomewhere near the existing host mapping. Two-line change.If preview-vs-original is also a design intent we want to expose, an
--original/--rendition <full|preview>flag onattachments downloadwould be a clean follow-up — but the immediate regression fix is just the host map.Workaround for downstream users
Until the SDK ships the fix, a small wrapper that does the URL rewrite + Bearer curl works around the issue. Happy to share the script if helpful.
Environment
basecamp version 0.7.2(fromcurl -fsSL https://basecamp.com/install-cli | bash, March 2026)app.basecamp.comReferences
attachments download+ URL rewrite in SDKCOMMUNIQUE-inline-attachments-api.md— documents the rewrite mechanism (public, in this repo)