PinAIConsole← L'application

Documentation de l'API

Une seule adresse de base, deux dialectes : Chat Completions pour les échanges textuels des clients compatibles OpenAI, et le dialecte natif pour les options propres à PinAI.

Démarrage

Tout part de la même adresse. Une clé se crée dans Clés API ; elle ne s'affiche qu'une fois.

https://ai.pinstudio.ch/api/v1

Dans un client compatible OpenAI — éditeur, extension, bibliothèque —, renseignez cette adresse comme base URL et votre clé comme API key. Sélectionnez le mode Chat Completions textuel.

curl https://ai.pinstudio.ch/api/v1/chat/completions \
  -H "Authorization: Bearer pinai_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bunny-1",
    "messages": [{ "role": "user", "content": "Bonjour." }]
  }'
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://ai.pinstudio.ch/api/v1",
  apiKey: process.env.PINAI_API_KEY,
});

const reponse = await client.chat.completions.create({
  model: "bunny-1",
  messages: [{ role: "user", content: "Bonjour." }],
});

console.log(reponse.choices[0].message.content);

Authentification

Un en-tête Bearer, sur toutes les routes. Il n'y a pas de clé publique : une clé PinAI s'utilise depuis un serveur, jamais depuis un navigateur ni une application mobile distribuée.

Authorization: Bearer pinai_…

Une clé porte les autorisations que vous lui donnez, et rien de plus. La génération exige ai.generate ; les connaissances d'équipe exigent en plus team_knowledge.read.

AutorisationDonne accès àRoutes
ai.generateGénérationLes deux dialectes, le catalogue et l'utilisation.
team_knowledge.readConnaissancesLe champ organization_id, qui joint les documents d'une équipe à la demande.
profile.readProfilRéservée aux intégrations OAuth.
team.readÉquipesRéservée aux intégrations OAuth.

Dialecte OpenAI

La route que connaissent les clients du commerce. Elle traduit vers la génération PinAI, sans rien décider d'autre.

POST/api/v1/chat/completions

ChampTypeDétail
modelchaîne, requisLe nom d'un assistant, par exemple bunny-1. Le catalogue est donné par GET /models.
messagestableau, requisRôles user, assistant, system, developer et tool. content accepte une chaîne ou des morceaux { type: 'text', text }. Un message tool porte tool_call_id ; un message assistant peut porter tool_calls sans contenu. 400 messages au plus ; 400 000 caractères pour l'ensemble, consignes et outils compris.
toolstableauVos fonctions, au format OpenAI : { type: 'function', function: { name, description, parameters } }. 128 au plus. PinAI n'exécute rien : il rend les appels décidés, vous les exécutez.
tool_choicechaîne ou objetauto, none, required, ou { type: 'function', function: { name } }. Transmis au modèle tel quel.
parallel_tool_callsbooléenÀ false, le modèle n'appelle qu'un outil à la fois.
temperaturenombreEntre 0 et 2. Sans valeur, celle de l'assistant.
max_tokensentierAlias accepté de max_completion_tokens. Ramené dans l'intervalle 64–8192 plutôt que refusé.
max_completion_tokensentierPlafond de la réponse. Même intervalle.
streambooléenfalse par défaut. À true, la réponse arrive en événements SSE.

Les messages system deviennent des consignes : elles se greffent sur l'identité de l'assistant au lieu de la remplacer. Un assistant sait toujours comment il s'appelle et qui l'édite, quelles que soient vos consignes.

{
  "id": "chatcmpl-…",
  "object": "chat.completion",
  "created": 1789000000,
  "model": "bunny-1",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "Bonjour." },
      "finish_reason": "stop"
    }
  ]
}
{
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_…",
            "type": "function",
            "function": { "name": "lire_fichier", "arguments": "{\"chemin\":\"src/index.ts\"}" }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}

En flux, les événements suivent la forme habituelle : un premier morceau portant le rôle, des morceaux de texte, un dernier portant finish_reason, puis [DONE]. La raison de fin vaut length si le plafond de sortie est atteint, ou content_filter si le fournisseur filtre la réponse. En cas de panne ou de flux incomplet, un événement error remplace la fin réussie. Un appel d'outil arrive dans un seul morceau, complet, indexé comme chez OpenAI : un argument JSON n'est exécutable qu'entier, et il n'y a donc rien à accumuler.

data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}], …}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Bon"},"finish_reason":null}], …}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"jour."},"finish_reason":null}], …}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}], …}
data: [DONE]

Dialecte PinAI

Le dialecte natif. Il donne accès à ce que le format OpenAI ne sait pas exprimer : l'effort de réflexion, les connaissances d'équipe, et ce qui reste de votre réserve.

POST/api/v1/responses

ChampTypeDétail
inputchaîne ou tableau, requisUne question, ou une conversation de messages { role, content } avec role valant user, assistant ou tool. Un message tool porte tool_call_id ; un message assistant peut porter tool_calls sans contenu. 400 000 caractères pour l'ensemble, consignes et outils compris, et 400 messages au plus.
assistant_idchaîneLe nom d'un assistant. Omis, celui de votre forfait répond. Nommé mais introuvable, la demande est refusée plutôt que servie par un autre.
instructionschaîneVos consignes. Elles s'ajoutent à l'identité de l'assistant, et comptent dans les 400 000 caractères du contexte.
organization_idchaîneJoint les documents de cette équipe à la demande. Exige team_knowledge.read et votre appartenance à l'équipe.
temperaturenombreEntre 0 et 2.
max_output_tokensentierEntre 64 et 8192.
effortchaîneauto, minimal, low, medium ou high. Sans valeur, celui de l'assistant.
toolstableauVos fonctions, au même format que le dialecte OpenAI. 128 au plus.
tool_choicechaîne ou objetauto, none, required, ou { type: 'function', function: { name } }.
parallel_tool_callsbooléenÀ false, le modèle n'appelle qu'un outil à la fois.
streambooléenÀ true, la réponse arrive en NDJSON : une ligne de JSON par événement.
session_idchaîneVotre propre identifiant de session, repris tel quel dans le premier événement du flux.
{
  "id": "resp_…",
  "object": "response",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [{ "type": "output_text", "text": "Bonjour." }]
    }
  ],
  "output_text": "Bonjour.",
  "finish_reason": "stop",
  "sources": [],
  "usage": { "remaining_percent": null, "credits_remaining_micros": 9998650 }
}

sources ne se remplit qu'avec organization_id : il nomme les documents d'équipe qui ont servi, et la page où ils ont été lus.

En flux, chaque ligne est un objet complet : response.created, puis des response.output_text.delta, puis response.completed. Une panne du fournisseur arrive comme une ligne error au milieu du flux, avec un statut HTTP déjà envoyé — vérifiez le type de chaque ligne plutôt que le seul code de réponse. La raison de fin se trouve dans response.finish_reason en flux, et dans finish_reason hors flux.

Avec tools, les appels décidés arrivent hors flux dans tool_calls, à côté de output_text, et en flux dans une ligne response.tool_calls puis dans response.completed. La raison de fin vaut alors tool_calls, et output_text peut être vide : le tour de parole est le vôtre.

Assistants

Le catalogue que votre accès peut appeler, et rien de plus : un assistant qui n'y figure pas sera refusé.

GET/api/v1/models

{
  "object": "list",
  "default_id": "bunny-1",
  "data": [
    {
      "id": "bunny-1",
      "object": "model",
      "name": "Bunny 1",
      "description": "Léger et économe.",
      "available": true
    }
  ]
}

id est le nom à passer dans model ou assistant_id. Une liste vide signifie qu'aucun assistant n'est ouvert à votre type d'accès — pour une clé d'intégration, c'est le cas tant qu'aucun tarif n'est publié. Les tarifs figurent page Modèles.

Utilisation

Ce qui reste, dans l'unité de la bourse qui paie vos appels.

GET/api/v1/usage

ChampTypeDétail
remaining_percentnombre ou nullLe pourcentage restant du cycle, pour un accès payé par le forfait. null pour un forfait sans limite, et pour un accès payé aux crédits.
credits_remaining_microsentierLe solde en micro-euros, pour un accès payé aux crédits. 1 000 000 micro-euros valent 1 €.
reset_atdate ISO ou nullLa fin du cycle en cours. null pour les crédits, qui ne se renouvellent pas.

Facturation

Deux bourses qui ne communiquent pas. Épuiser l'une ne coupe jamais l'autre.

AccèsCe qui paieCe qui se passe à l'épuisement
Clé d'intégrationCrédits prépayés402 insufficient_credits. Rechargez page Crédits.
Clé API dans PinCodeCrédits prépayés402 insufficient_credits. Rechargez page Crédits.
Compte PinAI connecté dans PinCodeBudget du forfait402 quota_error jusqu'au prochain cycle.
Intégration OAuthCrédits prépayés402 insufficient_credits.

Les tarifs s'expriment en euros par million de jetons, hors taxes, et diffèrent à l'entrée et à la sortie. Ils figurent page Modèles. Le prix d'un appel se calcule sur les jetons réellement consommés, pas sur le plafond demandé.

Erreurs

Toujours la même forme : un objet error avec un message lisible et un type stable.

{ "error": { "message": "Crédits insuffisants pour cette demande.", "type": "insufficient_credits" } }
StatutTypeCe qu'il faut faire
400invalid_request_errorLa requête ne respecte pas le format. Le message nomme le champ en cause.
401authentication_errorClé absente, invalide ou révoquée.
402quota_error · insufficient_creditsLa réserve ne couvre pas la demande. Attendez le cycle suivant, ou rechargez.
403permission_error · model_unavailableL'autorisation manque à la clé, ou l'assistant n'est pas ouvert à ce type d'accès.
404model_unavailableL'assistant demandé n'existe pas, ou n'est pas dans votre catalogue.
429rate_limit_errorTrop de requêtes. L'en-tête Retry-After dit combien de temps attendre.
502provider_errorPanne passagère côté génération. Réessayez.

Limites

Comptées par fenêtre glissante d'une minute, et cumulatives : la plus basse s'applique.

PortéeGénérationCatalogue et utilisation
Par clé30 / minute30 / minute
Par compte60 / minute—
Par adresse IP120 / minute—

Le délai de génération dépend du fournisseur et du client. PinCode impose un plafond de quinze minutes. Un flux interrompu par le client reste facturé pour ce qui a déjà été produit.