API REST

Envoyez des fichiers, créez des collections et gérez les partages via HTTP. Toutes les réponses sont au format JSON ; aucune clé API n’est requise pour les envois anonymes.

Introduction

L’API storage.to alimente notre CLI, application desktop, uploader web, et tout client tiers que vous souhaitez créer.

Le processus d’envoi se fait en trois étapes :

  1. Initialiser — dites-nous que vous souhaitez envoyer un fichier. Nous renvoyons une ou plusieurs URL pré-signées pointant vers Cloudflare R2.
  2. Envoyer vers R2 — :envoyez vos octets directement vers l(es) URL(s) pré-signée(s). Les octets ne passent pas par nos serveurs.
  3. Confirmer — dites-nous que l’envoi est terminé. Nous créons un enregistrement File et vous fournissons une URL partageable.

URL de base

https://storage.to/api

Tous les endpoints ci-dessous sont relatifs à cette base. Exemple : POST /upload/init signifie POST https://storage.to/api/upload/init.

Authentification

La plupart des endpoints ne nécessitent pas d’authentification. Les envois anonymes sont une fonctionnalité essentielle.

L’authentification est facultative et permet :

  • Envois associés à votre compte (visibles à /dashboard)
  • Fonctionnalités premium (fichiers permanents, stockage plus important)
  • Modifications basées sur la propriété (supprimer, définir un mot de passe, modifier l’expiration) sans avoir besoin que le visitor-token corresponde

Nous utilisons des jetons porteurs Laravel Sanctum. Obtenez un jeton via la passerelle OAuth du desktop ou la connexion web, puis envoyez-le comme :

Authorization: Bearer <token>

Jeton visiteur

Les clients anonymes ont besoin d’un moyen de prouver qu’ils sont bien propriétaires de leurs propres envois sans compte. Nous utilisons un visitor token — une chaîne aléatoire que le client génère une fois et réutilise. Envoyez-la avec chaque requête :

X-Visitor-Token: <random-string>

Sur le web, le jeton est stocké automatiquement dans le cookie visitor_token. La CLI le stocke à ~/.config/storageto/token (voir Docs CLI).

Pour les endpoints de mutation (suppression, définition du mot de passe, changement de l’expiration), la propriété est confirmée si soit le token du visiteur correspond à ou si la requête provient de la même IP que celle qui a créé le fichier. Les deux peuvent être perdus (cookies effacés, changements de réseau). À l’avenir, la preuve préférée est token du propriétaire.

Jeton propriétaire

Chaque endpoint qui crée une ressource (/upload/init multipart, /upload/confirm, /upload/reserve, /collection) renvoie un owner_token dans sa réponse. Le token est une preuve signée de propriété liée à cette ressource précise, indépendante de votre IP ou de votre token de visiteur.

Conservez le token avec l’ID de la ressource et envoyez-le pour toute mutation comme :

Authorization: Owner <token>

Ou, si vous utilisez déjà Authorization: Bearer pour une session authentifiée, envoyez-le comme :

X-Owner-Token: <token>

Le serveur accepte le token du propriétaire comme preuve de propriété valide en plus du mécanisme de secours historique visitor token + IP — les clients qui détiennent le token continuent de fonctionner après avoir changé de réseau ou effacé les cookies, et les clients qui ne l’ont pas fonctionnent exactement comme avant.

Les tokens durent aussi longtemps que la ressource, peuvent être conservés sans risque et n’expirent pas indépendamment. Un token perdu signifie perdre le contrôle de cette ressource (fichier/collection/téléversement) — traitez-les comme des mots de passe locaux.

Erreurs

Les erreurs suivent une structure cohérente :

{
  "success": false,
  "error": "Human-readable message"
}

Codes HTTP courants :

CodeSignification
200OK.
201Créé.
400Requête incorrecte (par ex. limite de taille de la collection dépassée).
401Mot de passe requis ou incorrect.
403Non autorisé (pas le propriétaire de la ressource).
404Ressource introuvable ou expirée.
422Échec de la validation, ou restriction liée au plan / au quota.
429Limite de débit atteinte ou quota de téléversement dépassé.
500Erreur serveur. Vérifiez statut.

Limites de débit

Toutes les limites de débit sont par IP. Une réponse 429 inclut les en-têtes standards Retry-After, X-RateLimit-Limit et X-RateLimit-Remaining.

PortéeLimite
Initialisation / confirmation / annulation du téléversement60 / minute
Finalisation multipart500 / minute
URLs des parties multipart120 / minute
Initialisation / confirmation en lot500 / minute
Vérifications de statut (fichier & collection)120 / minute
Paramètres (mot de passe, expiration, max de téléchargements)30 / minute
Vérification du mot de passe10 / minute
Création de collection30 / minute
Gérer (prêt, supprimer)60 / minute
Téléversement de la vignette120 / minute
Téléversement ShareX20 / jour
Analytique de l’app / erreurs120 et 60 / minute

Quota de téléversement : Les clients anonymes ont deux plafonds en parallèle — 100 Go / 24 h par visitor token et 500 Go / 24 h par IP (le plafond IP détecte le trafic sans jeton et les réseaux partagés). Quand l’un des deux est dépassé, vous obtenez un 429 avec des détails. C’est un quota téléversement uniquement — les téléchargements sont illimités et non limités (servis directement depuis des URL signées R2).

Téléverser

Le flux de téléversement en trois étapes pour n’importe quel fichier, y compris les fichiers de plus de 5 Go (multipart automatiquement). Si vous avez seulement besoin d’un téléversement rapide de type capture d’écran, regardez plutôt ShareX.

POST /upload/init 60/min

Démarrez un téléversement. Pour les fichiers >50 Mo, la réponse est un téléversement multipart (champ type: "multipart") ; sinon, un seul PUT pré-signé.

Corps de la requête

ChampTypeDescription
filenamestring · requiredNom de fichier original. Max 255 caractères.
content_typestring · requiredType MIME.
sizeinteger · requiredTaille du fichier en octets. Min 1.
Request
curl -X POST https://storage.to/api/upload/init \ -H "Content-Type: application/json" \ -H "X-Visitor-Token: abc123" \ -d '{ "filename": "report.pdf", "content_type": "application/pdf", "size": 2202009 }'
Response · single upload
{ "success": true, "type": "single", "upload_url": "https://r2.cloudflarestorage.com/...signed...", "headers": { "Host": ["..."] }, "r2_key": "uuid-abc123" }
Response · multipart
{ "success": true, "type": "multipart", "upload_id": "01HXYZ...", "r2_key": "uuid-abc123", "part_size": 33554432, "total_parts": 4, "initial_urls": { "1": "https://...", "2": "https://..." }, "owner_token": "owner_v1_..." }
POST /upload/parts Owner only 120/min

Demande des URL supplémentaires pour les parties d’un upload multipart en cours. Utilisé quand /init a renvoyé moins d’URL que vous n’avez de parties (ou qu’elles ont expiré).

Corps de la requête

ChampTypeDescription
upload_idstring · requiredL’ upload_id provenant de /init.
part_numbersarray<int> · requiredNuméros de parties pour lesquels obtenir des URL.
Request
curl -X POST https://storage.to/api/upload/parts \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ...", "part_numbers": [3, 4] }'
Response
{ "success": true, "part_urls": [ { "partNumber": 3, "url": "https://..." }, { "partNumber": 4, "url": "https://..." } ] }
POST /upload/complete-multipart Owner only 500/min

Finalise un upload multipart sur R2 une fois que toutes les parties sont téléversées.

Corps de la requête

ChampTypeDescription
upload_idstring · requiredL’ upload_id provenant de /init.
partsarray · requiredChaque entrée : { partNumber, etag } depuis la réponse R2.
Request
curl -X POST https://storage.to/api/upload/complete-multipart \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ...", "parts": [ { "partNumber": 1, "etag": "\"abc...\"" }, { "partNumber": 2, "etag": "\"def...\"" } ] }'
Response
{ "success": true }
POST /upload/abort Owner only 60/min

Annule un upload multipart et nettoie toute donnée partielle sur R2.

Corps de la requête

ChampTypeDescription
upload_idstring · requiredL’upload à annuler.
Request
curl -X POST https://storage.to/api/upload/abort \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ..." }'
POST /upload/confirm 60/min

Confirme que l’upload est terminé. C’est à ce moment que nous créons l’enregistrement File et renvoyons l’URL partageable.

Corps de la requête

ChampTypeDescription
filenamestring · requiredNom de fichier original.
sizeinteger · requiredTaille du fichier en octets.
content_typestring · requiredType MIME.
r2_keystring · requiredL’ r2_key provenant de /init.
collection_idstring · optionalAjouter à une collection.
crc32integer · optionalSomme de contrôle CRC32 pour la vérification d’intégrité.
file_idstring(9) · optionalRéalise (fulfil) un identifiant de fichier réservé précédemment créé.
Request
curl -X POST https://storage.to/api/upload/confirm \ -H "Content-Type: application/json" \ -H "X-Visitor-Token: abc123" \ -d '{ "filename": "report.pdf", "size": 2202009, "content_type": "application/pdf", "r2_key": "uuid-abc123" }'
Response
{ "success": true, "file": { "id": "FQxyz1234", "url": "https://storage.to/FQxyz1234", "raw_url": "https://storage.to/r/FQxyz1234", "filename": "report.pdf", "size": 2202009, "human_size": "2.1 MB", "expires_at": "2026-04-15T12:00:00Z" }, "owner_token": "owner_v1_..." }
POST /file/reserve 60/min

Réserve un identifiant de fichier et une URL partageable :avant que les octets soient prêts. Pratique quand vous devez d’abord partager un lien, puis finaliser l’upload ensuite. La propriété est liée à votre token de visiteur + IP. Terminez l’upload plus tard avec /upload/init + /upload/confirm, en passant file_id à la confirmation.

Corps de la requête

ChampTypeDescription
filenamestring · optionalNom de fichier fictif. Par défaut : "Pending".
content_typestring · optionalType MIME fictif.
Request
curl -X POST https://storage.to/api/file/reserve \ -H "X-Visitor-Token: abc123"
Response
{ "success": true, "file": { "id": "FQxyz1234", "url": "https://storage.to/FQxyz1234", "raw_url": "https://storage.to/r/FQxyz1234", "expires_at": "2026-04-12T18:00:00Z" }, "owner_token": "owner_v1_..." }
POST /upload/init-batch 500/min

Équivalent en lot de /upload/init, optimisé pour l’uploader web. Démarre jusqu’à 250 fichiers en un seul aller-retour.

Utilisé en interne par l’uploader web. La plupart des clients devraient préférer /upload/init pour un seul fichier.

POST /upload/confirm-batch 500/min

Équivalent en lot de /upload/confirm. Confirme de nombreux fichiers en un seul aller-retour.

Collections

Une collection regroupe plusieurs fichiers sous une seule URL de partage (/c/{id}). Jusqu’à 10 000 fichiers et 25 Go au total.

POST /collection 30/min

Créez une nouvelle collection. Ajoutez les fichiers ensuite en passant collection_id sur /upload/confirm.

Corps de la requête

ChampTypeDescription
expected_file_countinteger · optionalIndication pour marquer automatiquement la collection comme prête une fois que tous les fichiers attendus ont été confirmés.
Request
curl -X POST https://storage.to/api/collection \ -H "Content-Type: application/json" \ -H "X-Visitor-Token: abc123" \ -d '{ "expected_file_count": 3 }'
Response
{ "success": true, "collection": { "id": "ABC123xyz", "url": "https://storage.to/c/ABC123xyz", "expires_at": "2026-04-15T12:00:00Z" }, "owner_token": "owner_v1_..." }
GET /collection/{id}/status 120/min

Interroge l’état d’une collection. Marque aussi automatiquement la collection comme prête si tous les fichiers attendus ont été confirmés.

Request
curl https://storage.to/api/collection/ABC123xyz/status
Response
{ "success": true, "files": [ /* file objects: id, url, filename, size, ... */ ], "is_uploading": false, "file_count": 3, "expected_file_count": 3, "total_size": 6291456, "human_total_size": "6 MB" }
POST /collection/{id}/ready Owner only 60/min

Marque la collection comme prête au téléchargement. Pas généralement nécessaire — les collections deviennent automatiquement prêtes une fois expected_file_count atteint.

DELETE /collection/{id} Owner only 60/min

Supprime une collection et tous ses fichiers.

POST /collection/{id}/password Owner only 30/min

Définit un mot de passe pour la collection. Nécessite 4 à 100 caractères.

Corps de la requête

ChampTypeDescription
passwordstring · required4–100 caractères.
Request
curl -X POST https://storage.to/api/collection/ABC123xyz/password \ -H "X-Visitor-Token: abc123" \ -d '{ "password": "hunter22" }'
DELETE /collection/{id}/password Owner only 30/min

Retire le mot de passe d’une collection.

POST /collection/{id}/verify-password 10/min

Vérifie un mot de passe. Renvoie 200 en cas de succès, 401 si le mot de passe est incorrect.

Corps de la requête

ChampTypeDescription
passwordstring · required
POST /collection/{id}/expiry Owner only 30/min

Modifier l’expiration d’une collection.

Corps de la requête

ChampTypeDescription
daysinteger · optionalDans 1 à 7 jours. Omettez ou null pour une durée permanente (premium uniquement).
POST /collection/{id}/max-downloads Owner only 30/min

Définit une limite de téléchargements (burn-after-N-downloads). La collection est supprimée automatiquement une fois atteinte.

Corps de la requête

ChampTypeDescription
max_downloadsinteger · optional1–1000. Doit dépasser le nombre actuel de téléchargements. null pour supprimer la limite.

Fichiers

Tous les paramètres au niveau du fichier (mot de passe, expiration, max-downloads) reflètent les endpoints de la collection. Réservé au propriétaire.

GET /file/{id}/status 120/min

Vérifie si un fichier est encore en attente de son upload.

Response
{ "pending": false }
DELETE /file/{id} Owner only 60/min

Supprime un fichier immédiatement.

POST /file/{id}/thumbnail Owner only 120/min

Téléverse une image miniature pour un fichier vidéo ou image (utilisée sur la page de téléchargement). Max 2 Mo.

Corps de la requête

ChampTypeDescription
thumbnailimage · requiredUpload multipart. Max 2 Mo.
Response
{ "success": true, "thumbnail_url": "https://..." }
POST /file/{id}/password Owner only 30/min

Définit un mot de passe pour un fichier. Nécessite 4 à 100 caractères.

DELETE /file/{id}/password Owner only 30/min

Supprimer le mot de passe d’un fichier.

POST /file/{id}/verify-password 10/min

Vérifier le mot de passe d’un fichier.

POST /file/{id}/expiry Owner only 30/min

Modifier l’expiration d’un fichier.

Corps de la requête

ChampTypeDescription
daysinteger · optionalDans 1 à 7 jours. Omettez ou null pour une durée permanente (premium uniquement).
POST /file/{id}/max-downloads Owner only 30/min

Limiter le nombre total de téléchargements d’un fichier. Suppression automatique une fois atteint.

Téléversement ShareX

Endpoint d’upload en une seule fois — envoyez un fichier multipart, recevez une URL partageable. Pas de danse init/confirm. Idéal pour les outils de capture d’écran. Guide de configuration complet à /fr/docs/sharex.

POST /sharex/upload 20/day

Téléversez directement une image ou un fichier (formulaire multipart, champ file). Max 25 Mo.

Request
curl -X POST https://storage.to/api/sharex/upload \ -F "[email protected]"
Response
{ "success": true, "url": "https://storage.to/FQxyz1234", "raw_url": "https://storage.to/r/FQxyz1234", "filename": "screenshot.png", "expires_at": "2026-04-15T12:00:00Z" }

Authentification bureau

Pour les clients authentifiés (par ex. l’application desktop) disposant d’un token Sanctum.

GET /user Bearer token

Retourne l’utilisateur authentifié.

Request
curl https://storage.to/api/user \ -H "Authorization: Bearer <token>"
Response
{ "id": 42, "name": "Ada", "email": "[email protected]", "is_premium": true }
POST /auth/logout Bearer token

Révoquer le jeton d’accès actuel.

Divers

GET /health

Vérification de santé. Ping la base de données, le stockage R2 et le cache Redis. Renvoie 200 si tout est au vert, 503 sinon.

Response
{ "status": "healthy", "checks": { "database": "ok", "storage": "ok", "cache": "ok" }, "timestamp": "2026-04-12T12:00:00Z" }
GET /activity

Flux d’activité en direct pour le globe de la page d’accueil. Mis en cache sur les serveurs edge de Cloudflare.

GET /bandwidth/status 60/min

Utilisation actuelle du quota de téléchargement pour l’appelant — utilisée par la CLI et l’application de bureau pour afficher la capacité restante. La forme de la réponse diffère pour les utilisateurs authentifiés. Malgré le nom de l’URL, cela suit uniquement les téléversement octets ; les téléchargements ne sont pas comptés.

Response · anonymous
{ "success": true, "authenticated": false, "has_token": true, "limit_bytes": 107374182400, "limit_gb": 100, "used_bytes": 12345678, "used_gb": 0.01, "remaining_bytes": 107361836722, "remaining_gb": 99.99, "window_hours": 24 }
Response · authenticated
{ "success": true, "authenticated": true, "plan": "premium" }
POST /app-analytics 120/min

Envoyer un événement d’utilisation depuis la CLI ou l’application de bureau.

Corps de la requête

ChampTypeDescription
appstring · requireddesktop, cli ou web.
versionstring · optionalVersion du client.
eventstring · requiredNom de l’événement, par ex. upload_complete.
contextobject · optionalMétadonnées supplémentaires.
POST /app-errors 60/min

Envoyer un rapport d’erreur depuis la CLI ou l’application de bureau. Dédupliqué côté serveur — max 10 de la même erreur par heure.

Corps de la requête

ChampTypeDescription
appstring · requireddesktop, cli ou web.
typestring · requiredClasse/type d’erreur.
messagestring · requiredMessage d’erreur.
stackstring · optionalTrace de la pile.
version, os, os_version, arch, contextvarious · optionalMétadonnées de diagnostic.