Developer reference
REST under /api/external/v1 with your access key. When a gallery purchase fulfills, we POST a signed webhook to your endpoint.
API prefix: https://staging.snapselldev.eu/api/external/v1/
This page documents only:
https://staging.snapselldev.eu/api/external/v1 (galleries and file uploads).Issuing the API key, setting your webhook URL, and rotating keys are done inside the SnapSell product. You will receive a plaintext key once; store it securely. To request programmatic access from the app, the account must already be KYC verified and have a verified phone number
Send the full plaintext key on every request, using either:
Authorization: Bearer YOUR_KEY_HERE100 requests per minute per account. If you exceed this you'll get a 429 response with a retry_after value (in seconds) telling you when to try again.
{gallery_hash} is the gallery’s public hash returned when creating a gallery. {upload_id} is the integer id returned by POST /uploads/simple/init. All requests to our API require the Authorization: Bearer header — except the S3 PUT step, which uses only the presigned URL.
https://staging.snapselldev.eu/api/external/v1/galleriesReturns all galleries belonging to your account, paginated newest first. Each gallery includes its uploads and a ready-to-share payment_url. Galleries with no uploads are excluded.
page — page number (default 1)per_page — results per page; default 15, max 50{
"galleries": [
{
"id": 42,
"user_id": 7,
"title": "Summer pack",
"description": "Optional",
"price": "9.99",
"hash": "SEf4rXKf9y",
"status": "active",
"is_deleted": false,
"is_collection": false,
"is_adult_content": false,
"views": 0,
"created_at": "2026-06-13T11:07:46.000000Z",
"updated_at": "2026-06-13T11:07:46.000000Z",
"deleted_at": null,
"uploads_count": 2,
"payment_url": "https://staging.snapselldev.eu/buy/SEf4rXKf9y",
"uploads": [
{
"id": 101,
"product_id": "85de3406-3c8c-42ef-b0a7-d99b3b7fc249",
"user_id": 7,
"original_filename": "photo.png",
"mime_type": "image/png",
"file_size": 335872,
"file_path_original": "users/7/products/DbzK5gk8TwAK21gwct2GZF49r8qdIuYC",
"file_path_preview": null,
"status": "processed",
"storage_disk": "s3",
"local_backup_path": null,
"uploaded_to_s3_at": "2026-06-13T11:14:36.000000Z",
"s3_upload_attempts": 0,
"preview": null,
"thumbnail": null,
"created_at": "2026-06-13T11:13:42.000000Z",
"updated_at": "2026-06-13T11:17:28.000000Z",
"deleted_at": null,
"pivot": {
"gallery_id": 42,
"upload_id": 101,
"order": 0,
"created_at": "2026-06-13T11:17:28.000000Z",
"updated_at": "2026-06-13T11:17:28.000000Z"
}
}
]
}
],
"current_page": 1,
"last_page": 1,
"per_page": 15,
"total": 1
}
https://staging.snapselldev.eu/api/external/v1/galleriesCreate a new gallery. Returns the created gallery object and a checkout link your buyers can use to purchase.
title — required string, max 255price — required number, minimum 5description — optional stringis_collection — optional boolean (default false){
"title": "Summer pack",
"price": 9.99,
"description": "Optional"
}
{
"message": "Gallery created successfully.",
"gallery": {
"id": 42,
"user_id": 7,
"title": "Summer pack",
"description": "Optional",
"price": "9.99",
"hash": "8pmTfurIG3",
"status": "active",
"is_deleted": false,
"is_collection": false,
"is_adult_content": false,
"views": 0,
"created_at": "2026-06-13T11:22:05.000000Z",
"updated_at": "2026-06-13T11:22:05.000000Z",
"deleted_at": null
},
"payment_url": "https://staging.snapselldev.eu/buy/8pmTfurIG3"
}
https://staging.snapselldev.eu/api/external/v1/galleries/{gallery_hash}Fetch a single gallery with all its attached uploads.
{
"gallery": {
"id": 42,
"user_id": 7,
"title": "Summer pack",
"description": "Optional",
"price": "9.99",
"hash": "SEf4rXKf9y",
"status": "active",
"is_deleted": false,
"is_collection": false,
"is_adult_content": false,
"views": 0,
"created_at": "2026-06-13T11:07:46.000000Z",
"updated_at": "2026-06-13T11:07:46.000000Z",
"deleted_at": null,
"uploads": [
{
"id": 101,
"product_id": "85de3406-3c8c-42ef-b0a7-d99b3b7fc249",
"user_id": 7,
"original_filename": "photo.png",
"mime_type": "image/png",
"file_size": 335872,
"file_path_original": "users/7/products/DbzK5gk8TwAK21gwct2GZF49r8qdIuYC",
"file_path_preview": null,
"status": "processed",
"storage_disk": "s3",
"local_backup_path": null,
"uploaded_to_s3_at": "2026-06-13T11:14:36.000000Z",
"s3_upload_attempts": 0,
"preview": null,
"thumbnail": null,
"created_at": "2026-06-13T11:13:42.000000Z",
"updated_at": "2026-06-13T11:17:28.000000Z",
"deleted_at": null,
"pivot": {
"gallery_id": 42,
"upload_id": 101,
"order": 0,
"created_at": "2026-06-13T11:17:28.000000Z",
"updated_at": "2026-06-13T11:17:28.000000Z"
}
}
]
},
"payment_url": "https://staging.snapselldev.eu/buy/SEf4rXKf9y"
}
https://staging.snapselldev.eu/api/external/v1/uploads/simple/initStart a file upload. Returns a presigned S3 PUT URL valid for up to 24 hours. No multipart setup required — you send the entire file in one PUT. Save the upload.id and the url from this response for the next two steps.
original_filename — required string, max 512mime_type — required string, max 255file_size — required integer (bytes), 1 – 5,368,709,120 (5 GiB max){
"original_filename": "photo.png",
"mime_type": "image/png",
"file_size": 335872
}
{
"message": "PUT the file bytes to the url using the provided headers, then call complete.",
"upload": {
"id": 101,
"product_id": "472e30a4-111f-4274-87ee-f447ae635937",
"user_id": 7,
"original_filename": "photo.png",
"mime_type": "image/png",
"file_size": 335872,
"file_path_original": "users/7/products/DLbsWD2IWwZdlF60AyDb8ZPaj55bwi8U",
"file_path_preview": null,
"status": "pending_direct_upload",
"storage_disk": "s3",
"local_backup_path": null,
"uploaded_to_s3_at": null,
"s3_upload_attempts": 0,
"preview": null,
"thumbnail": null,
"created_at": "2026-06-13T11:24:59.000000Z",
"updated_at": "2026-06-13T11:24:59.000000Z",
"deleted_at": null
},
"url": "https://your-bucket.s3.region.amazonaws.com/users/7/products/DLbsWD2IWwZdlF60AyDb8ZPaj55bwi8U?X-Amz-Algorithm=AWS4-HMAC-SHA256&...",
"method": "PUT",
"headers": {
"Content-Type": "image/png"
}
}
{url} — directly to S3, not our APISend the raw file bytes directly to the presigned URL returned by init. This request goes straight to S3 — do not include your Authorization header here, the URL is already signed.
Content-Type header to the exact mime type you passed to init (e.g. image/png). Upload file as a binary file. The response body will be empty — a 200 OK status means the upload succeeded.
Content-Type: image/png — must match the mime_type from initEmpty body. A 200 status confirms the file reached S3.
https://staging.snapselldev.eu/api/external/v1/uploads/simple/{upload_id}/completeNotify the server that the S3 PUT succeeded. The server verifies the file exists on storage, marks the upload ready, and queues preview generation. The upload’s id is then ready to add to a gallery.
None required.
{
"message": "File uploaded to storage. Preview processing has started.",
"upload": {
"id": 101,
"product_id": "85de3406-3c8c-42ef-b0a7-d99b3b7fc249",
"user_id": 7,
"original_filename": "photo.png",
"mime_type": "image/png",
"file_size": 335872,
"file_path_original": "users/7/products/DbzK5gk8TwAK21gwct2GZF49r8qdIuYC",
"file_path_preview": null,
"status": "pending_processing",
"storage_disk": "s3",
"local_backup_path": null,
"uploaded_to_s3_at": "2026-06-13T11:14:36.000000Z",
"s3_upload_attempts": 0,
"preview": null,
"thumbnail": null,
"created_at": "2026-06-13T11:13:42.000000Z",
"updated_at": "2026-06-13T11:14:36.000000Z",
"deleted_at": null
}
}
Returned if the file is not yet found on storage — ensure the S3 PUT returned 200 before calling complete.
https://staging.snapselldev.eu/api/external/v1/galleries/{gallery_hash}/itemsAttach one or more uploads to a gallery. Each upload can only belong to one gallery — passing an upload that is already in a different gallery returns a 422 with the conflicting ids listed.
upload_ids — required array of 1–100 integers (upload.id values from complete){
"upload_ids": [101, 102]
}
{
"message": "2 uploads added to gallery successfully.",
"gallery": {
"id": 42,
"user_id": 7,
"title": "Summer pack",
"description": "Optional",
"price": "9.99",
"hash": "SEf4rXKf9y",
"status": "active",
"is_deleted": false,
"is_collection": false,
"is_adult_content": false,
"views": 0,
"created_at": "2026-06-13T11:07:46.000000Z",
"updated_at": "2026-06-13T11:07:46.000000Z",
"deleted_at": null,
"uploads": [
{
"id": 101,
"original_filename": "photo.png",
"mime_type": "image/png",
"file_size": 335872,
"status": "processed",
...
"pivot": { "gallery_id": 42, "upload_id": 101, "order": 0, ... }
},
{
"id": 102,
"original_filename": "video.mp4",
"mime_type": "video/mp4",
"file_size": 10485760,
"status": "processed",
...
"pivot": { "gallery_id": 42, "upload_id": 102, "order": 1, ... }
}
]
},
"payment_url": "https://staging.snapselldev.eu/buy/SEf4rXKf9y"
}
{
"message": "One or more uploads are already assigned to another gallery.",
"conflicting_upload_ids": [102]
}
https://staging.snapselldev.eu/api/external/v1/galleries/{gallery_hash}Permanently delete a gallery. The attached uploads are not deleted — they remain in your account and can be added to a new gallery.
{
"message": "Gallery deleted successfully."
}
After a buyer successfully completes a purchase, SnapSell sends a POST request to the webhook URL you configured in your dashboard. If no URL is set, nothing is sent. Failed deliveries are retried automatically.
POSTapplication/jsonv1= + lowercase hex HMAC-SHA256 of {timestamp}.{raw_body} using your webhook_signing_secret.SnapSell-Webhooks/1.0t = timestamp header; sig = hex after v1= on the signature header.t + "." + rawBody with your secret; compare lowercase hex to sig with a constant-time comparison.The payload below is what your server receives after every completed sale.
{
"id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"type": "gallery.sale.completed",
"api_version": "2026-04-07",
"created_at": "2026-04-07T12:00:00+00:00",
"data": {
"transaction_uuid": "...",
"gallery_id": 1,
"gallery_hash": "...",
"gallery_title": "...",
"amount": "19.99",
"currency_code": "EUR",
"seller_earning": "17.50",
"buyer_email": "buyer@example.com",
"payment_gateway": "stripe",
"completed_at": "2026-04-07T12:00:00+00:00"
}
}
| Code | Typical cause |
|---|---|
| 401 | Missing, malformed, or revoked API key; account no longer valid for key use. |
| 403 | Wallet / currency not configured; policy denies the resource. |
| 404 | Unknown gallery hash; upload id not owned by the key’s user. |
| 409 | Upload operation not allowed in the current upload state (e.g. completing an upload that was already aborted). |
| 422 | Validation errors; disallowed file type on init; invalid complete payload. |
| 429 | Too many requests. Wait before retrying. |
| 503 | Server error. Retry after a short wait; contact support if it persists. |
The typical integration flow is: upload your files → create a gallery → attach the files → share the payment link. The steps below show the complete sequence using cURL. Replace YOUR_KEY_HERE with your API key.
Call init to get a presigned S3 URL, PUT the file directly to S3, then call complete. Repeat for each file you want to sell.
1a. Init the upload
INIT=$(curl -sS -X POST "https://staging.snapselldev.eu/api/external/v1/uploads/simple/init" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY_HERE" \
-d '{
"original_filename": "photo.png",
"mime_type": "image/png",
"file_size": 335872
}')
UPLOAD_ID=$(echo $INIT | jq -r '.upload.id')
PUT_URL=$(echo $INIT | jq -r '.url')
1b. PUT the file directly to S3 (no auth header)
curl -sS -X PUT "$PUT_URL" \ -H "Content-Type: image/png" \ --upload-file /path/to/photo.png # Returns empty 200 OK — the file is now on S3
1c. Complete the upload
curl -sS -X POST "https://staging.snapselldev.eu/api/external/v1/uploads/simple/$UPLOAD_ID/complete" \ -H "Authorization: Bearer YOUR_KEY_HERE" # upload.status will be "pending_processing" — preview generation is queued
A gallery is the product your buyers purchase. Set a price and title, then add your files to it.
GALLERY=$(curl -sS -X POST "https://staging.snapselldev.eu/api/external/v1/galleries" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY_HERE" \
-d '{
"title": "Summer pack",
"price": 9.99,
"description": "My summer collection"
}')
GALLERY_HASH=$(echo $GALLERY | jq -r '.gallery.hash')
Pass one or more upload_id values collected from step 1. Each upload can only belong to one gallery.
curl -sS -X POST "https://staging.snapselldev.eu/api/external/v1/galleries/$GALLERY_HASH/items" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY_HERE" \
-d "{\"upload_ids\": [$UPLOAD_ID]}"
The payment_url returned in any gallery response is the checkout page for your buyers. You can also retrieve it any time by fetching the gallery.
curl -sS "https://staging.snapselldev.eu/api/external/v1/galleries/$GALLERY_HASH" \ -H "Authorization: Bearer YOUR_KEY_HERE" \ | jq -r '.payment_url' # https://staging.snapselldev.eu/buy/8pmTfurIG3
List all your galleries (page 1)
curl -sS "https://staging.snapselldev.eu/api/external/v1/galleries" \ -H "Authorization: Bearer YOUR_KEY_HERE"
List galleries — page 2, 30 per page
curl -sS "https://staging.snapselldev.eu/api/external/v1/galleries?page=2&per_page=30" \ -H "Authorization: Bearer YOUR_KEY_HERE"
Delete a gallery
curl -sS -X DELETE "https://staging.snapselldev.eu/api/external/v1/galleries/GALLERY_HASH" \ -H "Authorization: Bearer YOUR_KEY_HERE"