Parcourir la documentation

Requêtes et réponses

URL de base

https://api.freshperf.fr/v1. Toutes les routes de cette rubrique sont relatives à cette base, et seul HTTPS est servi.

Requêtes

RequêteRègle
CorpsJSON, avec Content-Type: application/json
Paramètres de cheminles identifiants publics du tableau, plus bas
Filtres et paginationparamètres de requête
Champs inconnus dans un corpsignorés

L'API refuse une valeur mal formée plutôt que de la corriger en silence :

CasRéponse
?limit=abc400 VALIDATION_ERROR:limit
?limit=0, ?limit=500400 VALIDATION_ERROR:limit
?cursor= illisible400 VALIDATION_ERROR:cursor
?status= hors énumération400 VALIDATION_ERROR:status
Champ de corps manquant ou hors énumération400 VALIDATION_ERROR:<champ>

Réponses

Un succès est une enveloppe :

{ "data": { "code": "SRV-TJUAQ1-1951", "status": "ACTIVE" } }

Une liste ajoute la pagination :

{
  "data": [ { "code": "SRV-TJUAQ1-1951", "status": "ACTIVE" } ],
  "pagination": { "nextCursor": "MTg4", "limit": 20 }
}
ChampTypeSens
pagination.nextCursorstring ou nullà repasser en ?cursor=. null sur la dernière page
pagination.limitentierla taille de page réellement appliquée

201 pour une création, 200 sinon. Les deux routes de PDF répondent le fichier, pas du JSON.

Pagination

?limit= vaut 1 à 100, 20 par défaut. Une valeur hors bornes répond 400 VALIDATION_ERROR:limit.

Le curseur est opaque et se lit sur deux mécaniques différentes selon la route. Ne le construisez pas vous-même.

MécaniqueRoutesComportement
Dernier identifiant vuGET /services, GET /orders, GET /invoicesune insertion pendant la marche ne décale pas les pages
DécalageGET /balance/transactions, GET /credit-notes, GET /ticketsune insertion pendant la marche décale les pages suivantes

Les listes vont du plus récent au plus ancien, sauf GET /tickets, trié sur la dernière mise à jour.

L'exception : les notifications

GET /account/notifications ne suit aucune de ces règles.

NotificationsRègle
Pagination?page= (1 par défaut, minimum 1) et ?limit= (20 par défaut, maximum 50)
Enveloppe{"data": {"items": [...], "total": n, "unread": n}}, sans bloc pagination

Erreurs

Toute erreur, sur toute route, a la même forme :

{
  "error": {
    "code": "API_SCOPE_MISSING:services.power:write",
    "message": "This API key does not hold the scope this operation needs.",
    "docs": "https://freshperf.fr/api-reference"
  }
}
  • code est un marqueur stable. Branchez votre code dessus.
  • message est une phrase en anglais pour les humains. Elle peut changer ; ne l'analysez pas.

Certains marqueurs portent un suffixe après deux-points :

MarqueurSuffixe
API_SCOPE_MISSING:<scope>la permission manquante
API_ACTION_NOT_ALLOWED:<action>l'action fournisseur refusée
VALIDATION_ERROR:<champ>le champ ou le paramètre fautif
PRODUCT_NOT_FOUND:<shortname>, PRODUCT_NOT_ORDERABLE:<shortname>, RECURRENCE_REQUIRED:<shortname>, RECURRENCE_NOT_OFFERED:<shortname>le produit de la ligne de commande
INVALID_METADATA_KEY:<clé>, INVALID_METADATA_VALUE:<clé>, MISSING_REQUIRED:<clé>la caractéristique de la ligne

SERVICE_NOT_FOUND n'a jamais de suffixe.

Classes de statut :

StatutSens
400la requête est mal formée ou l'état ne la permet pas
401clé absente ou inconnue
402un paiement n'est pas passé
403permission, plafond ou adresse
404inexistant, ou hors de la portée de la clé
409conflit avec l'état actuel, ou refus du fournisseur
429trop d'appels, ou geste trop fréquent
500de notre côté
502l'infrastructure derrière le service a échoué
503un PDF n'est pas encore prêt

La liste complète est dans Référence des erreurs.

Identifiants

RessourceChampFormeD'où il vient
ServicecodeSRV- + base36 + - + 4 chiffres, ex. SRV-TJUAQ1-1951GET /services
CommandeorderNumberORD-AAAAMMJJ-NNNN, ex. ORD-20260828-0001GET /orders, ou la réponse de POST /orders
FacturecodeINV-AAAAMM-NNNN, ex. INV-202608-0083GET /invoices
AvoircodeAV-AAAAMM-NNNN, ex. AV-202608-0001GET /credit-notes
TicketticketNumberTKT- + base36 + - + 4 chiffres, ex. TKT-TKH21B-6604GET /tickets, ou la réponse de POST /tickets
Moyen de paiementidentierGET /payment-methods
Clé SSHidentierGET /ssh-keys
SauvegardebackupIdchaîne propre au fournisseur, ex. 9b5d3938-cbd9-4946-8357-05badcdf6c33GET /services/{code}/backupsresult
PaiementidentierGET /invoices/{code}payments[]

Traitez-les comme opaques. Les identifiants d'infrastructure (instanceId, ownerId, vmid, nœud, cluster) sont retirés de toutes les réponses, à toute profondeur.

Montants et dates

  • Les montants sont des entiers en centimes : "totalCents": 1199 vaut 11,99 €. Jamais de flottant.
  • currency est un code ISO 4217, et peut valoir null sur un document sans montant.
  • Les dates sont des millisecondes depuis l'epoch, en UTC : "nextBillingAt": 1789437385056.
  • Les prix de catalogue et les prix récurrents d'un service sont hors taxe ; taxCents voyage à côté et totalCents inclut les deux.
  • amountCents d'un mouvement de solde est signé : positif au crédit, négatif au débit.

En-têtes

Que vous envoyez :

En-têteQuand
Authorization: Bearer fpk_...toujours
Content-Type: application/jsondès qu'il y a un corps
Idempotency-Keysur les quatre routes qui engagent de l'argent (voir Limites de débit et idempotence)

Que vous recevez :

En-têteQuandSens
X-Request-Idtoute réponseidentifiant de l'appel, présent aussi dans le journal de la clé. À citer au support
Cache-Control: no-storetoute réponse sauf /openapi.jsonrien ne se met en cache
X-RateLimit-Limit, -Remaining, -Resetréponses authentifiéesle budget le plus serré de la minute en cours. Reset est un horodatage en secondes epoch, pas un délai
Retry-After429, 409 IDEMPOTENCY_IN_PROGRESS, 503 PDF_NOT_AVAILABLEsecondes à attendre
WWW-Authenticate401Bearer realm="freshperf-api"
Idempotent-Replayed: truerejeula réponse vient d'un appel identique antérieur

GET /v1/openapi.json répond Cache-Control: public, max-age=300 avec un ETag, et ne porte pas les en-têtes de débit.

Seule l'origine du site FreshPerf est autorisée en CORS, pour la console d'essai de la référence. Une clé d'API n'a pas sa place dans une page web : appelez /v1 depuis vos serveurs.

Compatibilité

L'API est versionnée dans le chemin (/v1). Dans v1 nous ajoutons des champs, des routes et des valeurs d'énumération ; nous n'en retirons ni n'en renommons.

Écrivez donc des clients qui ignorent les champs inconnus et tolèrent une valeur d'énumération nouvelle. Un changement incompatible serait un /v2, annoncé à l'avance.