Aller au contenu

Exemples

Cette page regroupe des exemples d'appels à l'API REST pour les opérations les plus courantes. Ils utilisent curl ; quelques exemples en PHP et en Python figurent en fin de page.

Dans les exemples :

  • le jeton fictif iwal_xxxxxxxx est à remplacer par votre propre jeton d'accès ;
  • les identifiants (12345 pour un service, 678 pour un client…) sont fictifs : remplacez-les par ceux renvoyés par l'API ;
  • les réponses sont abrégées.

Pour éviter de répéter le jeton, enregistrez-le dans une variable d'environnement :

export IWAL_TOKEN="iwal_xxxxxxxx"
export IWAL_API="https://my.yulpa.io/api/v2"

Attention

Les appels sont réels : une création, une suppression ou une commande faite par l'API a le même effet que dans le manager iWal.

Vérifier son jeton

GET /v2/me (périmètre me:read) renvoie votre compte, ainsi que la méthode d'authentification et les périmètres du jeton utilisé :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/me"
{
  "data": {
    "id": 4321,
    "login": "utilisateur",
    "email": "contact@exemple.fr",
    "auth": { "method": "token", "token_id": 42, "scopes": ["services:read", "billing:read"] }
  },
  "meta": {}
}

Lister ses services

GET /v2/services (périmètre services:read) liste les services que vous pouvez voir. Les services résiliés sont masqués, sauf avec include_terminated=1.

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/services?per_page=100"

Filtres utiles :

Paramètre Effet
client_id Services d'un client (titulaire)
status Code de statut exact (par exemple 30 pour les services actifs)
category_id Services d'une catégorie
q Recherche sur le nom, la référence ou l'identifiant
include_terminated=1 Inclure les services résiliés
{
  "data": [
    {
      "id": 12345,
      "client_id": 678,
      "reference": "...",
      "name": "Hébergement exemple.fr",
      "status": { "code": 30, "label": "OK", "detail": "Le service est actif. Aucun problème à signaler." },
      "product": { "id": 10, "name": "...", "billing_frequency": "..." },
      "category": { "id": 3, "label": "...", "type": "hosting_pack" },
      "auto_renew_duration": 12,
      "expires_at": "2027-03-01 00:00:00"
    }
  ],
  "meta": { "page": 1, "per_page": 100, "total": 1, "total_pages": 1 }
}

Le détail d'un service (avec vos rôles sur ce service et ses services rattachés) s'obtient avec GET /v2/services/{id} :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/services/12345"

Astuce

Les libellés de tous les statuts sont disponibles avec GET /v2/meta/statuses?domain=service (valeurs de domain : service, user, right, client, orderform, billout).

Lire ses factures et bons de commande

Ces routes demandent le périmètre billing:read, et un rôle permettant de voir la facturation du client ou du service.

Lister les dernières factures :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/billouts?per_page=20"

Lister les bons de commande non payés (statut 2001) :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/orderforms?status=2001"

Les deux listes acceptent les filtres client_id, service_id (documents dont une ligne porte sur ce service), status (codes séparés par des virgules) et q (référence, référence comptable ou nom du titulaire).

Le détail d'une facture, avec ses lignes et ses paiements :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/billouts/98765"

Télécharger la facture au format PDF :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" \
  -o facture-98765.pdf "$IWAL_API/billouts/98765/pdf"

Les bons de commande ont les mêmes routes : GET /v2/orderforms/{id} et GET /v2/orderforms/{id}/pdf.

Zones DNS : consulter et ajouter un enregistrement

Les zones DNS s'utilisent à partir d'un service d'hébergement ou d'un nom de domaine enregistré chez YULPA. Il faut les périmètres hosting:read (lecture) et hosting:write (modification), et un rôle technique sur le service.

Lister les domaines et leurs zones

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/services/12345/dns/zones"
{
  "data": [
    {
      "domain_id": 555,
      "domain": "exemple.fr",
      "zone_id": 777,
      "status": 30,
      "secondaries": [],
      "records": 12
    }
  ],
  "meta": {}
}

Le champ domain_id sert à désigner le domaine dans les routes suivantes. status vaut 30 pour une zone active (null si le domaine n'a pas de zone).

Lire les enregistrements d'une zone

curl -s -H "Authorization: Bearer $IWAL_TOKEN" \
  "$IWAL_API/services/12345/dns/zones/555/records"
{
  "data": [
    { "id": 9001, "type": "A", "name": "www", "ttl": 0, "target": "203.0.113.10", "priority": 0, "weight": 0, "port": 0, "service": null, "protocol": null }
  ],
  "meta": { "zone_status": 30 }
}

Ajouter un enregistrement

Ajouter un enregistrement A pour intranet.exemple.fr :

curl -s -X POST -H "Authorization: Bearer $IWAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type": "A", "name": "intranet", "ttl": 3600, "target": "203.0.113.20"}' \
  "$IWAL_API/services/12345/dns/zones/555/records"

Champs d'un enregistrement :

Champ Description
type A, AAAA, CNAME, MX, TXT, SRV, NS ou CAA (obligatoire)
name Nom relatif à la zone, par exemple intranet pour intranet.exemple.fr (obligatoire)
target Cible : adresse IP, nom d'hôte ou texte (obligatoire)
ttl 0 (TTL de la zone, par défaut), 900, 1800, 3600, 7200, 10800, 14400, 21600, 28800, 43200, 86400 ou 172800
priority, weight MX et SRV
port, protocol SRV (protocol : _tcp, _udp)
service SRV (par exemple _sip) ou CAA (issue, issuewild, iodef)

L'API applique les mêmes contrôles que le manager (syntaxe selon le type, doublons, CNAME exclusif…). La réponse 201 renvoie l'enregistrement créé ; la zone est ensuite régénérée sur nos serveurs DNS.

Pour modifier un enregistrement, utilisez PUT /v2/services/{id}/dns/zones/{domain_id}/records/{record_id} avec les mêmes champs ; pour le supprimer, DELETE sur la même adresse :

curl -s -X DELETE -H "Authorization: Bearer $IWAL_TOKEN" \
  "$IWAL_API/services/12345/dns/zones/555/records/9002"

Pour en savoir plus sur les enregistrements DNS, voir Zones DNS.

Gérer une base MySQL

Ces routes concernent les bases MySQL d'un hébergement. Il faut les périmètres hosting:read et hosting:write, un rôle technique, et un service actif. Les créations et suppressions sont exécutées immédiatement sur le serveur MySQL.

Consulter les quotas et les serveurs

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/services/12345/mysql"

La réponse indique le préfixe ajouté automatiquement aux noms de bases et d'utilisateurs (prefix), les quotas (limits), l'utilisation (usage), les serveurs MySQL disponibles (servers, avec leur id) et la liste des droits attribuables (privileges_available).

Créer une base

curl -s -X POST -H "Authorization: Bearer $IWAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "boutique", "server_id": 3, "description": "Base de la boutique"}' \
  "$IWAL_API/services/12345/mysql/databases"

Le nom s'indique sans le préfixe ; server_id est l'identifiant d'un des serveurs renvoyés par GET /v2/services/{id}/mysql.

Créer un utilisateur

curl -s -X POST -H "Authorization: Bearer $IWAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "boutique", "server_id": 3, "password": "MotDePasse-Solide-2026", "password_confirmation": "MotDePasse-Solide-2026"}' \
  "$IWAL_API/services/12345/mysql/users"

Le champ host est facultatif (% par défaut). L'utilisateur doit être créé sur un serveur où l'hébergement a déjà une base active.

Donner des droits à l'utilisateur sur la base

curl -s -X POST -H "Authorization: Bearer $IWAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"database_id": 2001, "user_id": 3001, "privileges": ["ALL PRIVILEGES"]}' \
  "$IWAL_API/services/12345/mysql/privileges"

database_id et user_id sont les identifiants renvoyés à la création (ou par GET .../mysql/databases et GET .../mysql/users). privileges vaut ["ALL PRIVILEGES"] (tous les droits sauf GRANT) ou une liste de droits, par exemple ["SELECT", "INSERT", "UPDATE", "DELETE"].

Changer le mot de passe d'un utilisateur

curl -s -X PUT -H "Authorization: Bearer $IWAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"password": "Nouveau-MotDePasse-2026", "password_confirmation": "Nouveau-MotDePasse-2026"}' \
  "$IWAL_API/services/12345/mysql/users/3001/password"

Supprimer une base

curl -s -X DELETE -H "Authorization: Bearer $IWAL_TOKEN" \
  "$IWAL_API/services/12345/mysql/databases/2001"

Danger

La suppression d'une base est immédiate et définitive sur le serveur MySQL. Pensez à en faire une copie avant.

Pour en savoir plus, voir Bases MySQL.

Renouveler un service : devis puis commande

Le renouvellement se fait en trois étapes : repérer les services à renouveler, obtenir un devis, puis passer commande. Le bon de commande créé se paie ensuite comme d'habitude (voir Payer).

1. Trouver l'identifiant du client

curl -s -H "Authorization: Bearer $IWAL_TOKEN" "$IWAL_API/clients"

(périmètre clients:read). L'identifiant du client figure aussi dans chaque service (client_id).

2. Lister les services à renouveler

GET /v2/clients/{id}/renewals (périmètre billing:read) liste les services renouvelables du client, avec les durées possibles et leur prix. Par exemple, ceux qui arrivent à échéance dans moins de 30 jours :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" \
  "$IWAL_API/clients/678/renewals?expiring_days=30"
{
  "data": [
    {
      "service_id": 12345,
      "name": "Hébergement exemple.fr",
      "expires_at": "2026-12-01 00:00:00",
      "pending_order": false,
      "renewable": true,
      "durations": [
        { "duration": 12, "default": true, "total_ht": 100.0, "total_ttc": 120.0 }
      ]
    }
  ],
  "meta": { "vat_rate": 20 }
}

Les montants de cet exemple sont fictifs. pending_order indique qu'une commande est déjà en cours pour ce service.

3. Demander un devis

POST /v2/orders/quote (périmètre billing:read) calcule les lignes et les totaux, sans rien enregistrer :

curl -s -X POST -H "Authorization: Bearer $IWAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"client_id": 678, "items": [{"type": "renew", "service_id": 12345, "duration": 12}]}' \
  "$IWAL_API/orders/quote"

La réponse détaille chaque ligne (période couverte begins_at / ends_at, montants HT, TVA et TTC) et les totaux total_ht et total_ttc. En cas de problème sur un élément, l'erreur est indiquée dans errors (items.0, items.1…).

4. Passer commande

POST /v2/orders (périmètre billing:write) reçoit les mêmes données et crée le bon de commande. Avec send_mail, le bon de commande vous est aussi envoyé par mail :

curl -s -X POST -H "Authorization: Bearer $IWAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"client_id": 678, "items": [{"type": "renew", "service_id": 12345, "duration": 12}], "send_mail": true}' \
  "$IWAL_API/orders"

La réponse 201 contient l'identifiant (orderform_id), la référence, le statut et les montants du bon de commande. Un service ne peut figurer que dans une seule commande en cours : sinon, l'API répond 409.

Il reste à payer le bon de commande depuis le manager iWal (voir Payer et Renouvellements).

Astuce

Pour ne plus avoir à renouveler un service à la main, réglez son renouvellement automatique : PATCH /v2/services/{id} avec {"auto_renew_duration": 12} (périmètre services:write, 0 pour le désactiver).

Vérifier la disponibilité d'un nom de domaine

GET /v2/domains/check (périmètre billing:read) vérifie qu'un nom de domaine est valide et disponible, et indique le produit à commander :

curl -s -H "Authorization: Bearer $IWAL_TOKEN" \
  "$IWAL_API/domains/check?name=exemple.fr"

La réponse indique notamment :

  • action : create si le domaine est libre (achat), transfer s'il est déjà enregistré ailleurs (transfert possible) ;
  • product : le produit correspondant, ses durées possibles et son prix ;
  • requires : ce qu'il faudra fournir à la commande (identifiant WhoIs, code de transfert, acceptation de conditions particulières) ;
  • conditions : un texte si l'extension est soumise à des conditions du registre.

Si le nom n'est pas commandable, l'API répond 400 avec la raison dans errors.name.

Note

Chaque vérification interroge le registrar : elle est limitée à 60 vérifications par utilisateur toutes les 10 minutes.

Pour commander un nom de domaine, voir Commander un domaine.

Exemples en PHP et en Python

<?php
// Liste des services actifs, avec l'extension cURL de PHP
$token = getenv('IWAL_TOKEN'); // iwal_xxxxxxxx

$ch = curl_init('https://my.yulpa.io/api/v2/services?status=30&per_page=100');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $token,
        'Accept: application/json',
    ],
]);
$body   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

$json = json_decode($body, true);
if ($status !== 200) {
    // Erreur au format application/problem+json
    fwrite(STDERR, $json['title'] . ' : ' . ($json['detail'] ?? '') . PHP_EOL);
    exit(1);
}

foreach ($json['data'] as $service) {
    printf("%d\t%s\t%s\n", $service['id'], $service['name'], $service['expires_at'] ?? '-');
}
# Ajout d'un enregistrement DNS, avec la bibliothèque requests
import os
import requests

API = "https://my.yulpa.io/api/v2"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['IWAL_TOKEN']}"  # iwal_xxxxxxxx

response = session.post(
    f"{API}/services/12345/dns/zones/555/records",
    json={"type": "TXT", "name": "verification", "ttl": 3600, "target": "valeur-de-verification"},
)

if response.status_code == 201:
    print("Enregistrement ajouté :", response.json()["data"]["id"])
else:
    problem = response.json()  # application/problem+json
    print(problem["title"], problem.get("detail"), problem.get("errors"))

Parcourir toutes les pages d'une liste

import os
import requests

API = "https://my.yulpa.io/api/v2"
headers = {"Authorization": f"Bearer {os.environ['IWAL_TOKEN']}"}

page = 1
while True:
    r = requests.get(f"{API}/billouts", headers=headers, params={"page": page, "per_page": 100})
    r.raise_for_status()
    body = r.json()
    for billout in body["data"]:
        print(billout["reference"], billout["issued_at"], billout["amount_ttc"])
    if page >= body["meta"]["total_pages"]:
        break
    page += 1