spicyapiDocumentation
Référence
Contenu principal

Contrat Spicy Schema

Enveloppe de requête, champs canoniques, schéma de formulaire, cycle asynchrone, erreurs, stockage des résultats et compatibilité.

Spicy Schema est le contrat public de tout nouveau modèle média. Ajouter un modèle modifie son model, son inputSchema et son tarif client ; l’authentification, les tâches, les résultats, les erreurs et les téléchargements restent identiques.

Cette page sert de référence. La plupart des intégrations n’ont besoin que du Catalogue des modèles et schémas. Continuez si vous créez un formulaire, un validateur ou un générateur de code à partir d’inputSchema et voulez savoir exactement sur quoi vous appuyer.

Seul le catalogue actuel confirme qu’un modèle est appelable

Cette page définit la forme du contrat ; elle ne prétend pas qu’un modèle est en ligne. Avant toute tâche payante, lisez la réponse authentifiée de GET /api/v1/models et exigez à la fois enabled: true et available: true. Les entrées prévues ou désactivées ne sont pas appelables.

Frontière publique

Un ID public de modèle désigne une capacité stable sous la forme <publisher>/<model>/<task>. L’éditeur est le créateur du modèle : son auteur ou l’organisation de recherche à son origine. La retouche d’image prend toujours le suffixe edit, et les identifiants déjà publiés restent acceptés. Copiez toujours un véritable ID du catalogue actuel ; MODEL_ID_FROM_CATALOG est ici une valeur à remplacer.

Les réponses publiques, webhooks, erreurs et téléchargements utilisent uniquement les identifiants, codes d’erreur et URL de SpicyAPI. Ils ne contiennent jamais de détails internes de traitement, de réponses ou d’erreurs brutes ni de données commerciales internes. Les résultats sont copiés dans le stockage de SpicyAPI avant d’être renvoyés.

Enveloppe unifiée de création

{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": {
    "prompt": "slow camera push-in",
    "image_url": "spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3",
    "duration_seconds": 8,
    "resolution": "1080p",
    "generate_audio": true
  },
  "callBackUrl": "https://your-app.example.com/hooks/spicy"
}
ChampRègle
modelValeur exacte actuellement appelable du catalogue authentifié
inputDoit satisfaire l’inputSchema actuel de la même entrée ; les champs inconnus sont refusés
callBackUrlAdresse publique HTTPS facultative recevant la tâche terminée ; respectez exactement cette casse. Voir Webhooks

L’input ci-dessus est illustratif : seul l’inputSchema du modèle définit lesquels de ces champs sont acceptés.

Champs canoniques

Un même concept utilise un seul nom en snake_case dans tous les nouveaux points de terminaison. Ce tableau présente les champs courants, pas tous les champs ; l’inputSchema du modèle reste la liste complète.

ConceptChampType
Prompt positifpromptChaîne
Prompt négatifnegative_promptChaîne
Image principale / finaleimage_url / last_image_urlRéférence d’image, voir ci-dessous
Plusieurs images d’entréeimage_urlsTableau de références d’images
Masque de retouchemask_urlRéférence d’image
Vidéo / audio principalvideo_url / audio_urlURL HTTPS publique ou URI spicy:// validée
Médias de référencereference_image_urls / reference_video_urls / reference_audio_urlsTableau de références
Durée de sortieduration_secondsNombre ou entier
Résolution / ratioresolution / aspect_ratioÉnumération documentée
Dimensions exactes de sortiesize (préréglage), ou width / heightÉnumération documentée / entier
Fréquence d’imagesfpsEntier
Audio natifgenerate_audioBooléen
Seed aléatoireseedEntier
Poids d’adaptateurloras, ou high_noise_loras / low_noise_loras lorsqu’un point de terminaison expose ses étapes séparémentTableau d’objets
Nombre de sortiesnum_outputsEntier positif
Encodageoutput_formatÉnumération documentée
Conversation de chatmessagesTableau
Longueur de sortie / échantillonnagemax_tokens / temperatureEntier / nombre

Une référence d’image est une URL HTTPS publique, une URI spicy:// validée après envoi ou une petite URI data:image/…;base64,…. La vidéo et l’audio ne peuvent pas être intégrés directement. Voir Médias.

Une entrée de poids d’adaptateur désigne un ensemble de poids et l’intensité de son application. Le nom du champ est fixe, mais pas la forme de l’entrée : le sous-schéma items du point de terminaison précise ce qu’il accepte pour un fichier de poids, qu’il s’agisse d’une URL ou d’une clé de bibliothèque intégrée. Lisez-le au lieu de supposer un format. Il en va de même pour le nombre d’entrées et les sous-champs obligatoires.

Un champ propre à un modèle n’existe que s’il représente une capacité réelle qu’aucun champ canonique ne couvre déjà, et porte le nom de cette capacité. Un ancien point de terminaison peut conserver des noms de champs antérieurs dans son inputSchema actuel ; les nouveaux ne les reprennent pas.

inputSchema et annotations d’interface

Chaque modèle utilise JSON Schema 2020-12 dans inputSchema. Le même schéma pilote la validation serveur, les formulaires Playground, les exemples et les devis. Le SDK TypeScript officiel fournit des types statiques pour les enveloppes de requête et réponses communes ; l’input de chaque modèle reste un Record<string, unknown> à valider contre le schéma actuel, sans supposer des champs générés par modèle.

Ce site ne conserve aucune table de paramètres par modèle. Les paramètres ont une seule source : l’inputSchema renvoyé par GET /api/v1/models?includeSchema=1, également affiché par le Playground. Une copie statique diverge lorsque les modèles évoluent, sans moyen de détecter cette divergence. Pour construire votre propre table depuis cette source, voir Catalogue des modèles et schémas.

Chaque schéma reçu :

  • a une racine object et refuse les champs inconnus (additionalProperties: false) ;
  • précise required, les types, énumérations, plages, longueurs, limites de tableaux, formats d’URI, valeurs par défaut et conditions vérifiées ;
  • donne à chaque propriété une description anglaise non vide ;
  • omet les champs et valeurs par défaut non vérifiés plutôt que de les deviner ;
  • exprime les conditions par anyOf / allOf à la racine, voir ci-dessous, jamais par la seule visibilité dans l’interface ;
  • ne contient ni interrupteurs modifiant uniquement le traitement interne ni interrupteurs de filtrage des contenus.

Annotations de présentation : x-ui

Les métadonnées de présentation ne sont pas un second schéma de validation. Elles se trouvent toutes dans l’objet x-ui de la propriété, avec des clés en snake_case issues d’un ensemble fermé :

CléSignification
widgetContrôle à afficher ; valeurs ci-dessous
orderL’unique ordre des champs, valeurs décroissantes d’abord, unique dans un point de terminaison
label / placeholderLibellé remplaçant celui par défaut ; texte indicatif d’entrée
rowsNombre de lignes d’une zone de texte
step / unitPas numérique et suffixe d’unité (s, px)
accept / max_size_mbFiltre MIME et limite de taille par fichier des contrôles d’envoi
advancedAppartient à une section avancée repliée
primaryEntrée principale, épinglée en haut et jamais repliée
affects_priceModifier cette valeur nécessite un nouveau devis
visible_whenExpression de visibilité conditionnelle
enum_labelsAssociation entre valeur d’énumération et libellé affiché

x-ui.widget prend une des treize valeurs : textarea, text, upload, multi-upload, select, radio, slider, number, switch, json, object-list, chat-messages, hidden. Le widget correspond toujours à la forme des données, permettant de choisir directement le rendu : select et radio comportent toujours un enum ; slider comporte toujours minimum et maximum, jamais uniquement exclusiveMinimum / exclusiveMaximum ; switch est toujours un boolean ; multi-upload est un tableau avec items.type: string ; object-list est un tableau avec items.type: object et items.properties, chaque sous-champ portant ses propres type et description ; chat-messages est un tableau ; hidden signifie que le champ reste envoyé mais n’a pas de contrôle visible.

Pour tout tableau, ses propres minItems / maxItems et son sous-schéma items portent des contraintes que la liste racine required ne mentionne pas. Le serveur les applique comme le reste du schéma. Dans une object-list, items est lui-même un schéma d’objet complet : ses propres required, additionalProperties: false, et pour chaque sous-champ son type, sa plage et son default. Les valeurs par défaut s’appliquent aussi à ces entrées : un sous-champ absent est rempli depuis son default avant la validation. Un sous-champ obligatoire avec une valeur par défaut est donc facultatif en pratique. Un sous-champ sans valeur par défaut est refusé s’il manque, de même qu’un sous-champ inconnu ou une valeur hors de sa plage.

Un 400 lié à une valeur dans un tableau pointe vers l’entrée exacte, avec le champ, l’index commençant à zéro et le sous-champ, plutôt que vers toute la requête. Transmettez ce pointeur à la personne qui doit corriger : avec plusieurs entrées en cours, savoir laquelle dépasse la plage évite une recherche par élimination.

Les clients doivent ignorer les clés de présentation inconnues : ajouter une clé x-ui est rétrocompatible.

{
  "type": "object",
  "required": ["prompt", "duration_seconds"],
  "additionalProperties": false,
  "properties": {
    "prompt": {
      "type": "string",
      "minLength": 1,
      "description": "Describes the scene and its motion.",
      "x-ui": { "widget": "textarea", "order": 100, "primary": true, "rows": 4 }
    },
    "duration_seconds": {
      "type": "integer",
      "enum": [5, 8, 10],
      "description": "Output duration in seconds.",
      "x-ui": { "widget": "radio", "order": 90, "unit": "s", "affects_price": true }
    }
  },
  "x-pricing": { "variantFields": ["duration_seconds"], "billingMode": "variant_matrix" }
}

Chaque description est un texte anglais que vous pouvez afficher tel quel comme aide du champ.

x-ui.affects_price: true indique à l’interface d’actualiser un devis après modification du champ. Ce n’est pas une formule tarifaire et cela ne remplace jamais le pricing ou le quantityField du catalogue. À la racine, x-pricing représente côté serveur la même information : variantFields liste les champs qui sélectionnent un palier tarifaire et reste un sous-ensemble des champs affects_price: true, tandis que billingMode indique l’unité de facturation du point de terminaison. Ce sont des indications ; le montant faisant autorité est celui renvoyé par le catalogue, le devis et la tâche. La présentation des paliers dans pricing est expliquée dans Lire les tarifs.

Champs conditionnels

Les conditions prennent exactement deux formes, toutes deux à la racine :

{
  "anyOf": [{ "required": ["image_url"] }, { "required": ["image_urls"] }],
  "allOf": [
    { "if": { "required": ["last_image_url"] }, "then": { "required": ["image_url"] } },
    { "if": { "properties": { "resolution": { "const": "4k" } } }, "then": { "required": ["seed"] } }
  ]
}

Chaque branche anyOf contient uniquement required et signifie « fournissez au moins un de ces champs ». Chaque branche allOf est un if → then : if est déclenché par required ou properties.<field>.const, then ne déclare que required, et l’ensemble signifie « si vous fournissez A, vous devez fournir B ».

oneOf, not, dependentSchemas et dependentRequired n’apparaissent jamais dans l’inputSchema d’un modèle : ils expriment la contrainte mais ne peuvent pas être rendus comme un formulaire. L’utilisateur ne la découvrirait qu’au travers d’un 400 inexplicable après l’envoi. Implémenter les deux formes ci-dessus couvre tous les points de terminaison conditionnels.

Cycle de vie asynchrone

queued -> running -> succeeded
                  -> failed
                  -> expired

createTask renvoie un ID de tâche Spicy. Interrogez recordInfo ou recevez un webhook Spicy signé. queued et running ne sont pas terminaux ; les autres le sont. Les anciennes traces canceled restent lisibles pour compatibilité, mais les nouvelles tâches n’ont aucune action d’annulation. Tâches asynchrones fait autorité sur les états.

Un état non terminal inconnu dans un modèle est converti en running jusqu’à un état terminal connu ou l’échéance absolue de la plateforme. Une soumission non idempotente dont l’accusé de réception est incertain n’est pas renvoyée aveuglément, afin d’éviter les générations et facturations en double.

Réponse et erreurs unifiées

Chaque API JSON native (/api/v1) emploie la même enveloppe ; les points de terminaison compatibles /v1 et /v1beta utilisent les formats de leurs protocoles :

{
  "code": 200,
  "msg": "success",
  "data": {},
  "request_id": "req_01k3m8x9q2z4v7n5p6r8s0t1w2"
}

data est absent en cas d’échec synchrone. Utilisez code pour décider, jamais le msg modifiable ; utilisez request_id pour le support. Un échec après création de la tâche utilise data.state: "failed" et un errorCode stable. errorMessage est destiné à la lecture humaine : si le service du modèle a fourni un motif précis, il est repris en anglais, après suppression des noms, adresses et détails de compte internes. Sinon, c’est un message de plateforme dans votre langue d’erreur. Utilisez errorCode pour décider, jamais errorMessage.

Résultats et téléchargements

Une tâche réussie liste ses fichiers dans output.assets[]. Chaque fichier prêt comporte déjà un lien de téléchargement :

{
  "assets": [
    {
      "key": "tasks/2026/08/31/job_01k3m8x9q2z4v7n5p6r8s0t1w3/asset_01.mp4",
      "url": "https://example.r2.cloudflarestorage.com/results/asset_01.mp4?X-Amz-Signature=SIGNATURE_FROM_RESPONSE",
      "expiresAt": "2026-08-31T10:20:00Z",
      "mime": "video/mp4",
      "durationSeconds": 8,
      "bytes": 1234567
    }
  ]
}

Téléchargez directement url, sans votre clé API. Elle dure normalement 20 minutes ; relisez la tâche pour en obtenir une nouvelle tant que le résultat est conservé, au maximum 14 jours. Pendant le stockage d’un fichier, l’élément porte pending: true sans url ; si le stockage a définitivement échoué, il porte unavailable: true. key identifie le fichier pour le point de terminaison facultatif download-url ; il n’est pas nécessaire au téléchargement. Tous les liens pointent vers le stockage de SpicyAPI.

Facturation client

Les champs pricing, startingPrice et quantityField du catalogue, ainsi que estimatedCost / cost des tâches, décrivent uniquement les tarifs client. Les montants sont des chaînes décimales en USD. Les données commerciales internes sont hors du contrat public et n’apparaissent jamais dans les catalogues, tâches, webhooks ou journaux exportés.

Compatibilité et dépréciation

Les changements à tolérer sans rupture sont un nouveau champ facultatif, un champ de réponse que vous pouvez ignorer ou une indication x-ui inconnue.

Un nouveau champ obligatoire, une valeur d’énumération retirée, un changement de sens ou de type, une limite plus stricte ou un ID public de modèle modifié s’accompagne d’une nouvelle version de modèle ou d’un préavis de dépréciation annoncé. Les IDs publiés restent immuables ; les migrations utilisent un alias compatible ou une redirection explicite.

Mettez en cache le catalogue authentifié comme indiqué dans Mise en cache du catalogue. Ne gardez jamais de copie permanente du schéma et ne vous rabattez pas sur une page statique lorsque le catalogue actuel ne contient aucun élément appelable.

Sur cette page