Downloads API
Reference for the product download endpoint: signed URL generation, purchase verification, free vs paid access rules, and the R2 storage architecture.
This is an internal reference for contributors working on the MyClaude codebase. This endpoint is consumed by the web application and CLI — it is not a public developer API.
Product files are never served directly to the browser. Every download goes through a single endpoint that verifies authorization, checks purchase status for paid products, generates a time-limited signed URL, and tracks the download server-side.
Download a product file
Generate a signed URL for downloading a product's file. The URL expires after 5 minutes.
POST /products/downloadAuth: Required (Bearer token) Rate limit: 30/min (strict)
Request body
{
"productId": "abc123"
}| Field | Type | Required | Description |
|---|---|---|---|
productId | string | Yes | ID of the product to download |
Response
{
"url": "https://r2-signed-url.example.com/products/my-skill/my-skill.zip?X-Amz-...",
"fileName": "my-skill.zip"
}| Field | Type | Description |
|---|---|---|
url | string | Presigned download URL. Valid for 5 minutes. |
fileName | string | Original file name for the download |
The client should open this URL in a new tab or initiate a download. The URL becomes invalid after expiration or a single use (depending on storage provider configuration).
Access rules
The server applies different authorization rules based on the product's price and the requester's relationship to the product.
| Scenario | Access granted | Verification |
|---|---|---|
| Free product, any authenticated user | Yes | Auth token only |
| Paid product, product author | Yes | Auth token + author UID match |
| Paid product, buyer with completed order | Yes | Auth token + order lookup in orders collection |
| Paid product, no purchase | No | Returns 403 Purchase required |
| Unpublished product, author | Yes | Auth token + author UID match |
| Unpublished product, non-author | No | Returns 404 Product not found |
Download flow
Client sends POST /products/download
|
v
Verify Bearer token (authentication)
|
v
Check user is not banned
|
v
Fetch product from database
|
+-- Not found or unpublished (non-author) --> 404
|
v
Is product paid AND requester is not author?
|
+-- Yes --> Check orders collection for completed purchase
| |
| +-- No order found --> 403 "Purchase required"
| |
| +-- Order exists --> Continue
|
+-- No (free or author) --> Continue
|
v
Extract storage key from fileUrl
|
+-- Path traversal detected --> 400
|
v
Generate signed R2 URL (5-min expiry)
|
v
Track download (fire-and-forget):
+-- Write per-user download record (for review eligibility)
+-- Increment product stats.downloads counter
|
v
Return { url, fileName }Storage architecture
MyClaude uses a hybrid storage model. Product files are stored on Cloudflare R2 (S3-compatible object storage). Presigned URLs are generated server-side using R2 credentials -- the bucket is never directly exposed to clients.
File path format
products/{product-key}/{file-name}.zipExamples:
products/code-review-skill/code-review-skill.zip
products/my-agent/my-agent-v2.zipPath security
The server applies strict path validation before generating a signed URL:
| Check | Rejects |
|---|---|
Must start with products/ | Paths outside the products directory |
No .. sequences | Directory traversal attempts |
No // sequences | Double-slash path manipulation |
If the stored fileUrl is in a legacy format (Firebase Storage download URL or gs:// URI), the server extracts the storage key automatically.
Why signed URLs
| Concern | Solution |
|---|---|
| Prevent hotlinking | URLs expire after 5 minutes |
| Access control | Server verifies purchase before generating URL |
| No client-side secrets | R2 credentials stay server-side |
| Direct download | File is served from R2 edge, not proxied through the API |
Download tracking
Every successful download triggers two asynchronous writes (fire-and-forget, non-blocking):
| Write | Collection | Purpose |
|---|---|---|
| Per-user download record | products/{productId}/downloads/{uid} | Tracks which users downloaded. Used for review eligibility -- free products require a download before the user can submit a review. |
| Download counter increment | products/{productId}.stats.downloads | Global download count displayed on product pages and used for sorting. |
These writes use merge: true semantics, so repeated downloads by the same user update the timestamp without creating duplicates.
Errors
| Status | Condition |
|---|---|
| 400 | Missing productId, invalid file reference, or path traversal detected |
| 401 | Missing or invalid bearer token |
| 403 | Paid product without a completed purchase order, or user is banned |
| 404 | Product not found, not published (for non-authors), or no file associated |
| 429 | Rate limit exceeded (30 downloads/min) |
| 500 | Invalid file reference format (legacy URL parsing failure) |
Example: download via CLI
# Authenticate first
$ myclaude login
# Download a product by slug
$ myclaude install code-review-skillThe CLI resolves the slug to a product ID, calls POST /products/download, and saves the file to the appropriate directory.
Programmatic example
# Get a Firebase ID token (example using firebase-tools)
TOKEN=$(firebase auth:export-token)
# Request a signed download URL
curl -X POST https://myclaude.sh/api/products/download \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"productId": "abc123"}'
# Response:
# { "url": "https://...", "fileName": "my-skill.zip" }
# Download the file
curl -L -o my-skill.zip "SIGNED_URL_FROM_RESPONSE"Related pages
- API Overview -- Auth model, rate limits, error format
- Payments API -- How purchases are created (prerequisite for paid downloads)
- Products API -- Upload and scan flow (before download is possible)
- Purchasing Guide -- Buyer-facing purchase and download walkthrough
- Security Model -- Signed URL and storage security details
Payments API
Reference for payment endpoints: Stripe Checkout, Stripe Connect onboarding, webhook handling, and the secure purchase flow.
Agent Resources
Everything an AI agent needs to discover, install, and publish products on MyClaude — context payloads, machine-readable specs, MCP tools, and behavioral directives.