Gérer vos services
Toutes les routes de cette page prennent le code du service (SRV-TJUAQ1-1951), donné par GET /services. Un service hors de la restriction de la clé répond 404 SERVICE_NOT_FOUND.
GET /services
Permissions : services:read
| Paramètre | Type | Requis | Valeurs |
|---|---|---|---|
limit | entier | non, défaut 20 | 1 à 100 |
cursor | string | non | pagination.nextCursor de la page précédente |
status | string | non | ACTIVE, SUSPENDED, CANCELLED, DELETED |
curl -H "Authorization: Bearer $FRESHPERF_KEY" \ "https://api.freshperf.fr/v1/services?limit=2&status=ACTIVE"
{ "data": [ { "code": "SRV-TJUAQ1-1951", "name": "Minecraft 1", "customName": null, "productShortname": "minecraft-1", "productTitle": "Minecraft 1", "category": "minecraft", "status": "ACTIVE", "providerType": "pterodactyl", "autoRenew": true, "nextBillingAt": 1789437385056, "recurrence": "monthly", "createdAt": 1786845385066, "activatedAt": 1786845385056 } ], "pagination": { "nextCursor": null, "limit": 2 } }
| Champ | Type | Notes |
|---|---|---|
code | string | l'identifiant de toutes les routes de service |
name | string | le nom personnalisé s'il existe, sinon le titre du produit |
customName | string ou null | le nom que vous avez donné |
productShortname | string | à repasser en product dans une commande |
productTitle | string ou null | libellé du produit |
category | string ou null | shortname de la catégorie, ex. vps, minecraft |
status | string | ACTIVE, SUSPENDED, CANCELLED, DELETED |
providerType | string ou null | vps ou pterodactyl |
autoRenew | booléen | |
nextBillingAt | entier ou null | millisecondes epoch |
recurrence | string ou null | cycle de facturation, ex. monthly |
createdAt, activatedAt | entier ou null | millisecondes epoch |
GET /services/{code}
Permissions : services:read
Reprend les champs ci-dessus et ajoute :
| Champ | Type | Notes |
|---|---|---|
provisioned | booléen | false tant que l'infrastructure n'existe pas |
priceCents | entier | prix récurrent d'un cycle, hors taxe |
taxCents | entier | taxe du même cycle |
currency | string ou null | ISO 4217 |
dunningState | string | NONE, RETRY, SUSPENSION_TRIGGERED, DELETION_TRIGGERED |
restartRequired | booléen | un changement de ressources attend un redémarrage |
restartRequiredReason | string ou null | |
configuration | objet string → string | la configuration commandée. Les identifiants de connexion et les clés internes en sont retirés |
cancelledAt | entier ou null | millisecondes epoch |
{ "data": { "code": "SRV-TIYMZT-8349", "name": "VPS 1", "status": "ACTIVE", "providerType": "vps", "provisioned": true, "autoRenew": true, "nextBillingAt": 1787960297645, "recurrence": "monthly", "priceCents": 500, "taxCents": 100, "currency": "EUR", "dunningState": "NONE", "restartRequired": false, "restartRequiredReason": null, "configuration": { "os": "debian", "osVersion": "13", "location": "paris", "cpuCores": "2", "ramSize": "2048", "diskSize": "20" }, "cancelledAt": null } }
GET /services/{code}/status
Permissions : services.metrics:read
Interroge l'infrastructure en direct. Comptez jusqu'à deux secondes de latence.
Tous les champs peuvent valoir null : chaque fournisseur remonte ce qu'il connaît.
| Champ | Type | Notes |
|---|---|---|
status | string | texte du fournisseur : running, stopped, starting, stopping, restoring... Ce n'est pas une énumération fermée |
uptimeSeconds | entier | |
cpu | nombre | fraction ou pourcentage selon le fournisseur |
cpuCount | nombre | |
memoryBytes, memoryMaxBytes | entier | octets |
diskBytes, diskMaxBytes | entier | octets |
networkInBytes, networkOutBytes | entier | octets |
ipv4 | string | pour un serveur de jeu, adresse:port |
ipv6 | string | |
extraIpv4 | tableau de string | adresses supplémentaires |
hostname | string | |
extra | objet | les chiffres propres au fournisseur. Contenu libre, non versionné |
INSTANCE_NOT_PROVISIONED (409) tant que l'infrastructure n'existe pas.
GET /services/{code}/metrics/history
Permissions : services.metrics:read
{ "data": { "samples": [ { "ts": 1787903520000, "cpu": 0.1364, "mem": 1069400678, "maxmem": 2147483648, "netin": 4550, "netout": 69 } ] } }
samples est un tableau d'objets libres : la fenêtre, le pas et les clés viennent du fournisseur. Le tableau est vide quand aucun historique n'est disponible, notamment sur les serveurs de jeu.
POST /services/{code}/power
Permissions : services.power:write
Débit : 20 par minute, par clé et par service.
| Champ | Type | Requis | Valeurs |
|---|---|---|---|
action | string | oui | start, stop, restart, kill |
kill n'existe que sur les serveurs de jeu ; sur un VPS il répond 400 ACTION_UNSUPPORTED.
curl -X POST -H "Authorization: Bearer $FRESHPERF_KEY" \ -H "Content-Type: application/json" -d '{"action":"restart"}' \ https://api.freshperf.fr/v1/services/SRV-TJUAQ1-1951/power
{ "data": { "status": "SUCCESS", "message": "Power signal accepted" } }
| Champ | Valeurs |
|---|---|
status | SUCCESS quand c'est fait, PENDING quand l'infrastructure y travaille encore |
message | phrase du fournisseur, ou null |
Sur PENDING, relisez /status toutes les 5 à 10 secondes.
| Erreur | Statut | Quand |
|---|---|---|
VALIDATION_ERROR:action | 400 | action absent ou hors liste |
ACTION_UNSUPPORTED | 400 | kill sur un VPS |
SERVICE_NOT_ACTIVE | 409 | service suspendu ou résilié |
INSTANCE_NOT_PROVISIONED | 409 | pas encore d'infrastructure |
POST /services/{code}/console/command
Permissions : services.console:write
Serveurs de jeu uniquement.
| Champ | Type | Requis |
|---|---|---|
command | string | oui, non vide |
curl -X POST -H "Authorization: Bearer $FRESHPERF_KEY" \ -H "Content-Type: application/json" -d '{"command":"say bonjour"}' \ https://api.freshperf.fr/v1/services/SRV-TJUAQ1-1951/console/command
{ "data": { "status": "SUCCESS", "message": "Command sent" } }
Sur un VPS : 400 ACTION_UNSUPPORTED. Sur un serveur arrêté, le panneau refuse la commande et la réponse est 502 ACTION_FAILED.
Sauvegardes
Les quatre routes relaient le fournisseur. La réponse a toujours la même forme : {"data": {"status", "message", "result"}}, où result est la charge utile du fournisseur.
GET /services/{code}/backups
Permissions : services.backups:read
Répond même sur un service suspendu.
{ "data": { "status": "SUCCESS", "message": "OK", "result": { "customerEnabled": true, "limits": { "max": 6, "used": 2 }, "customer": [ { "id": "9b5d3938-cbd9-4946-8357-05badcdf6c33", "note": null, "status": "COMPLETED", "createdAt": "2026-08-15T19:50:45.282411", "sizeBytes": 21474837581 } ], "internal": [] } } }
Le id d'une sauvegarde est une chaîne, propre au fournisseur. C'est ce que prennent la suppression et la restauration.
POST /services/{code}/backups
Permissions : services.backups:write
| Champ | Type | Requis | Valeurs |
|---|---|---|---|
note | string ou null | non | 200 caractères au plus |
{ "data": { "status": "PENDING", "message": "VPS backup in progress", "result": { "taskId": "d3b780a2-308f-40bf-95a2-0316d8872721", "status": "PENDING" } } }
| Erreur | Statut | Quand |
|---|---|---|
NOTE_TOO_LONG | 400 | note au-delà de 200 caractères |
SERVICE_NOT_ACTIVE | 400 | service suspendu ou résilié |
SERVICE_IN_DUNNING | 400 | service en recouvrement |
INSTANCE_NOT_PROVISIONED | 400 | pas encore d'infrastructure |
LIMIT_REACHED | 409 | quota de sauvegardes atteint |
BACKUP_IN_PROGRESS | 400 | une sauvegarde ou une restauration tourne déjà |
DELETE /services/{code}/backups/{backupId}
Permissions : services.backups:write
backupId vient de result.customer[].id. Un identifiant inconnu répond 400 BACKUP_NOT_FOUND.
POST /services/{code}/backups/restore
Permissions : services.backups:write
Écrase le disque.
| Champ | Type | Requis | Valeurs | D'où il vient |
|---|---|---|---|---|
source | string | oui | CUSTOMER ou INTERNAL | - |
backupId | string | oui si source vaut CUSTOMER | - | result.customer[].id |
volid | string | oui si source vaut INTERNAL | - | result.internal[].volid |
Mêmes refus que la création, plus 400 MISSING_FIELDS quand source est absent ou que l'identifiant qui va avec manque.
POST /services/{code}/reinstall
Permissions : services.reinstall:write
Débit : 2 par minute, par clé et par service. Les appels refusés comptent.
Détruit le disque. Sur un serveur de jeu, remet le serveur à zéro et le corps peut être vide.
| Champ | Type | Requis | Valeurs | D'où il vient |
|---|---|---|---|---|
os | string | oui sur un VPS | valeur composite <famille>-<version> | GET /catalog/products/{shortname} → caractéristique de kind: "os-picker", champ options |
authType | string | oui sur un VPS | PASSWORD ou SSH_KEY | - |
authValue | string | oui sur un VPS | le mot de passe, ou une ligne de clé publique OpenSSH | - |
authValue est stocké chiffré, n'est jamais renvoyé, et cet appel n'est jamais journalisé avec son corps.
curl -X POST -H "Authorization: Bearer $FRESHPERF_KEY" \ -H "Content-Type: application/json" \ -d '{"os":"debian-13","authType":"SSH_KEY","authValue":"ssh-ed25519 AAAAC3Nza... admin@laptop"}' \ https://api.freshperf.fr/v1/services/SRV-TIYMZT-8349/reinstall
{ "data": { "status": "PENDING", "message": "reinstalling" } }
| Erreur | Statut | Quand |
|---|---|---|
MISSING_FIELDS | 400 | un des trois champs manque sur un VPS |
INVALID_OS | 400 | os hors des valeurs du catalogue |
OS_FAMILY_MISMATCH | 400 | famille et version incompatibles |
INVALID_AUTH_TYPE | 400 | authType hors liste |
SERVICE_NOT_ACTIVE, SERVICE_IN_DUNNING, INSTANCE_NOT_PROVISIONED | 400 | état du service |
API_RATE_LIMITED | 429 | plus de 2 appels dans la minute |
GET /services/{code}/addons
Permissions : services.addons:read
{ "data": { "addons": [ { "shortname": "extra-saves", "title": "Sauvegardes supplémentaires", "kind": "BACKUP_SLOTS", "status": "ACTIVE", "quantity": 1, "unitPriceCents": 399, "unitTaxCents": 80, "linePriceCents": 399, "lineTaxCents": 80, "nextCycleLinePriceCents": 399, "nextCycleLineTaxCents": 80, "pendingRemovalQuantity": 0 } ], "available": [ { "shortname": "extra-ram-1gb", "title": { "fr-fr": "+1 Go RAM", "en-us": "+1 Gb RAM" }, "kind": "RESOURCE_DELTA", "resourceKey": "ramSize", "unitDelta": 1024, "unitPriceCents": 200, "maxQuantity": 10 } ], "recurringTotalCents": 479 } }
| Champ | Contenu |
|---|---|
addons | les options que le service porte déjà |
available | celles qu'il peut encore prendre. maxQuantity à 0 veut dire sans limite |
recurringTotalCents | le total récurrent du service, options comprises, hors taxe |
Ajouter ou retirer une option se fait dans le tableau de bord.
PATCH /services/{code}
Permissions : services:write
| Champ | Type | Requis | Valeurs |
|---|---|---|---|
customName | string ou null | non | 32 caractères au plus. La chaîne vide efface le nom |
autoRenew | booléen | non | true uniquement |
curl -X PATCH -H "Authorization: Bearer $FRESHPERF_KEY" \ -H "Content-Type: application/json" -d '{"customName":"prod-eu-1"}' \ https://api.freshperf.fr/v1/services/SRV-TJUAQ1-1951
Répond le service complet, comme GET /services/{code}.
| Erreur | Statut | Quand |
|---|---|---|
CUSTOM_NAME_TOO_LONG | 400 | au-delà de 32 caractères |
RENEWAL_CANCELLATION_NOT_AVAILABLE_VIA_API | 400 | autoRenew: false. Arrêter un renouvellement se fait dans le tableau de bord |
POST /services/{code}/renew
Permissions : services:write et billing.invoices:pay, plus billing.saved_methods:charge dès que le financement touche une carte.
En-tête obligatoire : Idempotency-Key.
Débite le cycle suivant tout de suite et avance la date de facturation.
| Champ | Type | Requis | Valeurs | D'où il vient |
|---|---|---|---|---|
method | string | non, défaut account_default | account_default, balance, saved_method | - |
paymentMethodId | entier | oui si saved_method | > 0 | GET /payment-methods → id |
method | Effet |
|---|---|
account_default | applique la règle du compte : le solde s'il est opté et suffisant, sinon le moyen de paiement par défaut. Le passage par la carte exige billing.saved_methods:charge |
balance | le solde prépayé seul. Aucun repli sur une carte |
saved_method | débite le moyen désigné |
curl -X POST -H "Authorization: Bearer $FRESHPERF_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: renew-SRV-TJUAQ1-1951-2026-08" \ -d '{"method":"balance"}' \ https://api.freshperf.fr/v1/services/SRV-TJUAQ1-1951/renew
{ "data": { "status": "CHARGED", "serviceCode": "SRV-TJUAQ1-1951", "nextBillingAt": 1793091315189, "amountCents": 360 } }
status ne vaut que CHARGED : toute autre issue est une erreur. amountCents est le montant débité, taxe comprise.
| Erreur | Statut | Quand |
|---|---|---|
VALIDATION_ERROR:method | 400 | method hors liste |
PAYMENT_METHOD_ID_REQUIRED | 400 | saved_method sans paymentMethodId |
PAYMENT_METHOD_NOT_FOUND | 404 | ce moyen n'est pas sur le compte |
INSUFFICIENT_BALANCE | 402 | solde trop court |
NO_PAYMENT_METHOD | 402 | account_default sans moyen utilisable |
PAYMENT_METHOD_NOT_ACTIVE | 402 | moyen expiré ou bloqué |
RENEWAL_FAILED | 402 | le débit a échoué pour une autre raison |
API_KEY_SPENDING_CAP_EXCEEDED | 403 | le plafond de la clé ne couvre pas le cycle |
SERVICE_NOT_ACTIVE | 409 | service ni actif ni réactivable |
RENEWAL_IN_PROGRESS | 409 | un renouvellement du même service tourne déjà |
RENEWAL_TOO_FREQUENT | 429 | moins d'une minute depuis le précédent |
POST /services/{code}/actions/{action}
Permissions : elles dépendent de l'action envoyée.
La passerelle brute vers le fournisseur. Elle sert au pare-feu et au DNS inverse, qui n'ont pas de route dédiée.
| Action | Permission |
|---|---|
power, start_vps, stop_vps, reboot_vps | services.power:write |
send_console_command | services.console:write |
backup_list | services.backups:read |
backup_create, backup_delete, backup_restore | services.backups:write |
firewall_list | services.firewall:read |
firewall_create, firewall_update, firewall_move, firewall_delete | services.firewall:write |
rdns | services.rdns:write, y compris pour op: "get" |
features | services:read |
Aucune autre action n'est joignable. Il n'existe pas de firewall_add, firewall_set ni firewall_remove : une règle se crée, se modifie et se supprime avec firewall_create, firewall_update et firewall_delete.
Les actions de panneau de jeu (files, databases, allocations, schedules, startup, versions, content, subdomains, sftp, subusers) répondent 403 API_ACTION_NOT_ALLOWED quelles que soient les permissions de la clé.
| Champ | Type | Requis |
|---|---|---|
arguments | objet | non. Contenu propre à chaque action |
curl -X POST -H "Authorization: Bearer $FRESHPERF_KEY" \ -H "Content-Type: application/json" \ -d '{"arguments":{"op":"set","address":"203.0.113.224","hostname":"vps.exemple.fr"}}' \ https://api.freshperf.fr/v1/services/SRV-TIYMZT-8349/actions/rdns
{ "data": { "status": "SUCCESS", "message": "Action completed", "result": { "configured": true, "entries": [ { "family": "ipv4", "address": "203.0.113.224", "hostname": "vps.exemple.fr", "zoneConfigured": true } ] } } }
rdns prend op valant get, set ou delete, plus address (une adresse rendue par /status) et, pour set, hostname. Les arguments des actions firewall_* dépendent de l'hyperviseur : lisez firewall_list pour voir la forme d'une règle avant d'en écrire une.
| Erreur | Statut | Quand |
|---|---|---|
INVALID_ACTION | 400 | action vide ou commençant par _ |
API_ACTION_NOT_ALLOWED:<action> | 403 | action inconnue de la plateforme, ou sans permission publique |
SERVICE_NOT_ACTIVE | 409 | service suspendu ou résilié |
marqueur du fournisseur, ex. FIREWALL_INVALID_ARGUMENT | 409 | le fournisseur a refusé |
ACTION_FAILED | 502 | le fournisseur a échoué ou n'a pas répondu |
Préférez les routes typées (/power, /backups, /reinstall, /console/command) partout où elles existent : elles sont stables et indépendantes du fournisseur.