Downloads API
Reference for the product download endpoint: signed URL generation, purchase verification, free vs paid access rules, and the R2 storage architecture.
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 (Firebase Admin verifyIdToken)
|
v
Check user is not banned
|
v
Fetch product from Firestore
|
+-- 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.