Documentation
L’API REST
Tout ce que vous faites dans le panneau, votre propre code peut le faire aussi. Une clé, une URL de base et exactement les droits que vous avez déjà. Rien ici n’est un raccourci qui contourne le panneau.
URL de base et authentification
Chaque chemin de cette page se place derrière l’URL de base ci-dessous, et chaque requête transporte votre clé dans un en-tête Authorization. Il n’y a pas d’autre entrée : pas de cookie de session, pas d’authentification basique, pas de clé dans la chaîne de requête.
curl -s https://app.provibr.com/api/v1/me \
-H "Authorization: Bearer pvk_..."Les clés
Vous créez une clé sur votre propre page de profil, sous « Clés d’API ». Elle vous appartient et n’appartient qu’à un seul compte : elle n’est pas partagée avec vos collègues, et elle cesse de fonctionner dès que vous quittez ce compte.
- Une clé expire au bout de 90 jours, sauf choix contraire. Vous pouvez retenir une durée plus courte, ou aucune ; une clé sans date de fin est une clé que vous devez penser à révoquer vous-même.
- La rotation consiste à créer une nouvelle clé et à révoquer l’ancienne, avec un moment intermédiaire où les deux fonctionnent. On ne peut pas remplacer le secret d’une clé existante sur place, car cela vous laisserait sans clé utilisable si quelque chose tournait mal à mi-parcours.
- Vous pouvez lier une clé à une liste d’adresses. Une requête venue d’ailleurs est refusée avant que quoi que ce soit ne soit lu, et ce refus n’apprend rien à l’appelant sur la clé.
- Un membre détient au maximum 25 clés. Une clé révoquée conserve sa ligne : vous pouvez donc voir après coup ce qu’elle avait le droit de faire de son vivant.
Ce qu’une clé a le droit de faire
Une clé porte des droits : lecture ou écriture, par partie du panneau. Ce qu’elle peut réellement faire est l’intersection de ces droits et de ceux du membre à qui elle appartient.
Les droits sont lus en direct, à chaque requête. Retirez quelque chose à un membre et toutes ses clés se restreignent aussitôt, sans que personne ait à révoquer quoi que ce soit. C’est aussi pour cela qu’une clé survit à un changement de rôle au lieu d’être invalidée par lui.
Tout ce que fait un agent prend du temps
Votre infrastructure n’est joignable que par l’agent installé dans votre propre réseau, et cet agent se connecte vers l’extérieur. Un endpoint qui modifie une machine ne peut donc pas répondre par le résultat : il répond que l’ordre a été accepté et vous remet de quoi en suivre la suite.
- Vous envoyez la requête et recevez une réponse d’acceptation, avec un en-tête Location qui pointe vers la commande créée. Cet en-tête est absolu à partir de l’hôte : résolvez-le par rapport à l’origine et non par rapport à l’URL de base ci-dessus, sous peine de demander un chemin qui n’existe pas.
- Cette même commande figure aussi dans le corps, mais enveloppée dans un champ command. La relecture ci-dessous la renvoie nue, sans cette enveloppe : déballez-la une fois et la première relecture devient le plus souvent inutile.
- Vous relisez la commande jusqu’à ce que son statut soit définitif : terminée, échouée, expirée ou annulée. Seul en attente ne l’est pas, et un statut que vous ne connaissez pas compte lui aussi comme définitif, si bien qu’une valeur ajoutée plus tard ne vous laissera jamais relire indéfiniment.
- Une commande échouée porte le code d’erreur de l’agent lui-même, avec ses paramètres, les mêmes que ceux que le panneau affiche à votre écran.
# 1. the order - answers 202 with a Location header
curl -s -D - -X POST https://app.provibr.com/api/v1/servers/<serverId>/actions/start \
-H "Authorization: Bearer pvk_..."
# 2. the outcome - repeat until the status is final
curl -s https://app.provibr.com/api/v1/commands/<commandId> \
-H "Authorization: Bearer pvk_..."Ce que vous recevez
Chaque point de terminaison indique ce qu’il renvoie : 200 pour une lecture, 201 lorsqu’une ressource a été créée, 202 lorsque votre agent a encore du travail. Et tous décrivent le corps qu’ils renvoient, avec un nom propre par ressource, si bien que le code que vous générez contient un type par ressource et non un par point de terminaison.
La pagination
Les listes répondent par une page et un curseur ; vous renvoyez ce curseur jusqu’à ce qu’il revienne vide. Une page contient 50 lignes par défaut, et 100 au maximum.
Les erreurs
Toute réponse qui n’est pas un succès a la même forme, quelle que soit la nature du problème et quel qu’en soit l’endroit.
{
"error": {
"code": "validation_failed",
"message": "Name is too long (max 64).",
"detail": "server.validation.apiKeys.nameTooLong",
"params": { "max": 64 },
"requestId": "3f9c1b2a-..."
}
}code- Le mot sur lequel brancher votre code. Il vient d’une liste fermée : une valeur que vous ne connaissez pas signifie donc que vous parlez à une version plus récente que celle pour laquelle vous avez développé.
message- Une phrase en anglais, pour qui lit un journal. N’y branchez jamais votre code : la formulation peut changer à tout moment.
detail- Un identifiant stable, lorsqu’il y en a un. C’est celui que le panneau utilise pour composer son propre message : vous pouvez donc rédiger la phrase dans votre langue.
params- Les paramètres qui accompagnent cet identifiant : une limite dépassée, un nom déjà pris, l’hôte qui a refusé.
requestId- L’identifiant de cette requête, présent aussi dans un en-tête de réponse. Citez-le lorsque vous nous interrogez sur quelque chose.
Combien vous pouvez demander
Vous pouvez faire 120 requêtes par minute et par clé, et 600 par compte, toutes clés confondues. Cette seconde limite existe pour que vingt clés ne puissent pas imposer vingt fois la charge au même hôte.
Répéter une requête sans risque
Tout ce qui crée quelque chose accepte un en-tête idempotency-key que vous composez vous-même. Répétez la même requête avec la même valeur et vous récupérez le premier résultat, au lieu d’une deuxième machine, d’une deuxième clé ou d’une deuxième commande.
Vous pouvez aussi envoyer votre propre x-request-id. Il revient dans la réponse et apparaît dans nos journaux : c’est le moyen le moins coûteux de poser une ligne de votre côté à côté d’une ligne du nôtre.
Les fournisseurs
Il n’y a pas un seul nom de fournisseur dans le routage de cette API. Quels systèmes vous pouvez connecter, quels champs leurs identifiants comportent et ce dont ils sont capables : c’est l’endpoint de découverte qui répond, et le corps que vous envoyez pour créer une intégration est construit à partir de ce même registre. Un fournisseur qui entre en service apparaît dans les deux sans qu’un seul endpoint change.
Cela fonctionne aussi dans l’autre sens. Demandez à une machine quelque chose que son fournisseur ne sait pas faire (une console sur un compte d’hébergement, un disque sur un serveur de jeu), et vous obtenez un refus qui nomme la capacité manquante, avant que quoi que ce soit ne parte vers votre agent.
Tous les endpoints
Regroupés comme la spécification les regroupe. Chaque chemin vient après l’URL de base, et à côté figure le droit que l’endpoint exige ; là où rien ne figure, une clé valide suffit.
Self
The key itself: what it may do, and managing the keys of this member.
GET /api-keysapi-keys:manageList the API keys of the member behind this key, including revoked ones.POST /api-keys201api-keys:manageCreate an API key. The secret is returned once and never again.DELETE /api-keys/{keyId}api-keys:manageRevoke an API key belonging to the member behind this key.GET /dashboardThe dashboard tiles as numbers: servers, agents, capacity, credit, incidents, notifications.GET /meDescribe the organization, member and permissions behind this API key or app session.GET /organizationsList every account this member can work in, oldest membership first.POST /organizations/{organizationId}/activateSwitch the active account of this session. Returns the account you are now in.POST /ws-ticketMint a single-use ticket for the realtime WebSocket.
Discovery
What this platform can integrate with. Read this first if you build forms: a provider that goes live shows up here without any change to the endpoints below.
GET /integration-typesagents:readList the integration types this platform can manage, with their capabilities.
Servers
Virtual machines, hosting accounts and game servers. Everything that changes a machine is asynchronous: you get 202 with a command to poll.
GET /serversservers:readList the servers this key may see, oldest first.POST /servers202servers:writeOrder a server: a VM on a hypervisor, a hosting account on a control panel, or a game server on a game panel. Returns 202 with the command to poll.DELETE /servers/{serverId}202servers:writeDelete a server. Returns 202 with a command when the provider is asked to remove it, or 200 when only the platform record is removed.GET /servers/{serverId}servers:readFetch one server, including its agent, integration, capabilities and actions.POST /servers/{serverId}/actions/{verb}202servers:writeRun a power action on a server (start, stop, shutdown, suspend, resume, restart, kill). Returns 202 with the command to poll.POST /servers/{serverId}/actions/network-install202servers:writeInstall a server over the network with a PXE profile. Returns 202 with the command to poll.POST /servers/{serverId}/actions/refresh-firewall-log202servers:writeAsk the agent for the most recent firewall log lines. Returns 202; the lines are in the command result.POST /servers/{serverId}/actions/refresh-firewall-rules202servers:writeAsk the agent to read the current firewall rules and digest. Returns 202; poll the command, then read GET /firewall-rules.POST /servers/{serverId}/actions/refresh-hardware202servers:writeAsk the agent to read the current disk and NIC layout. Returns 202; poll the command, then read GET /hardware.POST /servers/{serverId}/actions/reinstall202servers:writeReinstall a server from an OS template: same vmid, same IP, everything on disk is lost. Returns 202 with the command to poll.POST /servers/{serverId}/actions/sync-ip-filter202servers:writePush the allocated IP addresses into the VM firewall ipset. Returns 202 with the command to poll.GET /servers/{serverId}/addressesservers:readList the IP addresses currently allocated to this server.POST /servers/{serverId}/addressesservers:writeAllocate an IP address to this server, from a pool or a specific address. Synchronous: returns 200 with the allocation and whether the VM firewall picked it up.DELETE /servers/{serverId}/addresses/{allocationId}servers:writeRelease an IP allocation back to the pool. Synchronous: returns 200 and whether the VM firewall picked it up.GET /servers/{serverId}/commandsservers:readList the commands issued against this server, newest first.POST /servers/{serverId}/disks202servers:writeAttach an extra disk to a server. Returns 202 with the command to poll.DELETE /servers/{serverId}/disks/{disk}202servers:writeDetach a disk, optionally destroying the volume. Returns 202 with the command to poll.PATCH /servers/{serverId}/disks/{disk}202servers:writeGrow a disk. Use "boot" as the disk key to grow the boot disk. Returns 202 with the command to poll.GET /servers/{serverId}/firewall-rulesservers:readReturn the last observed firewall rules and the platform's ipfilter intent.PUT /servers/{serverId}/firewall-rules202servers:writeReplace the customer-owned firewall rules. The digest from the last GET is required and acts as an optimistic lock. Returns 202 with the command to poll.GET /servers/{serverId}/hardwareservers:readReturn the most recent hardware observation (disks, NICs, firewall state).GET /servers/{serverId}/metricsservers:readRead the cpu, memory, disk and network series of one server for a window.POST /servers/{serverId}/nics202servers:writeAttach an extra network interface. Note that this turns the VM ipfilter off while the new NIC has no addresses. Returns 202 with the command to poll.DELETE /servers/{serverId}/nics/{nic}202servers:writeDetach a network interface. net0 is protected. Returns 202 with the command to poll.
Commands
The poll side of every asynchronous endpoint. A 202 hands you a command id and a Location header; this is where you read how it ended.
GET /commandsservers:readagents:readList commands across the organization, newest first.GET /commands/{commandId}servers:readagents:readPoll one command: its state, result and error code.
Agents
The agents that run inside your own network and do the actual work.
GET /agentsagents:readList the agents of this account, oldest first.POST /agents201agents:writeCreate an agent. The activation code and the installation one-liner are returned once and never again.DELETE /agents/{agentId}agents:writeRemove an agent from the panel. Only possible when it is pending or revoked and has no integrations or servers left.GET /agents/{agentId}agents:readGet one agent, including whether it is connected right now.PATCH /agents/{agentId}agents:writeChange the name and/or the description of an agent.POST /agents/{agentId}/actions/reissue-install-linkagents:writeIssue a new activation code and installation one-liner for this agent. Any outstanding enrollment token is superseded; the secrets are returned once.POST /agents/{agentId}/actions/revokeagents:writeRevoke an agent: it loses its certificate and wipes its identity and credential vault. Repeating this is a no-op.POST /agents/{agentId}/actions/update202agents:writeTell an agent to update itself to the release on this environment. Returns 202 with the command to poll.
Integrations
The connections from an agent to your infrastructure. Credentials are delivered to the agent once and are never readable back.
GET /integrationsagents:readList the integrations of this account, oldest first.POST /integrations202agents:writeCreate an integration and deliver its credentials to the agent. Returns 202 with the credential push to poll.DELETE /integrations/{integrationId}agents:writeDelete an integration. Refuses while servers are still attached.GET /integrations/{integrationId}agents:readGet one integration, with what the provider behind it can do.PATCH /integrations/{integrationId}agents:writeUpdate the name, provider settings, placement limits or enabled state. Credentials cannot be changed here.POST /integrations/{integrationId}/actions/import-unmanagedagents:writeImport resources that exist on the remote system but not in Provibr, from an earlier unmanaged listing.POST /integrations/{integrationId}/actions/list-remote202agents:writeAsk the agent to list something on the remote system (templates, plans, eggs, unmanaged resources). Which kinds exist is in the integration's remoteListings. Returns 202 with the command to poll.POST /integrations/{integrationId}/actions/resend-credentials202agents:writeSend a complete new set of credentials to the agent. Returns 202 with the credential push to poll.POST /integrations/{integrationId}/actions/test202agents:writeAsk the agent to test the connection to this system. Returns 202 with the command to poll.GET /integrations/{integrationId}/install-methodagents:readGet the current install method, or — with ?method= — what switching to another one would do. Changes nothing.PUT /integrations/{integrationId}/install-methodagents:writeSet the install method of this integration. Refuses when published, installable operating systems would lose their source.GET /integrations/{integrationId}/os-linksagents:readList the operating systems linked to this integration, with the provider template and/or PXE profile behind each.DELETE /integrations/{integrationId}/os-links/{osId}agents:writeClear one source for this operating system. The other source stays. Clearing a source that was not set is not an error.PUT /integrations/{integrationId}/os-links/{osId}agents:writeSet one source (provider template or PXE profile) for this operating system on this integration. Leaves the other source untouched.
IPAM
Address space: groups, prefixes, allocations, reservations, child blocks and VLAN pools.
DELETE /ipam/allocations/{allocationId}ipam:writeRelease an address allocation or reservation. Sets released_at; the row stays as history.GET /ipam/child-policiesipam:read⚑ ipamAutoAllocationList the child-prefix policies of this account, oldest first.POST /ipam/child-policies201ipam:write⚑ ipamAutoAllocationCreate a child-prefix policy. Returns 201 with the policy.DELETE /ipam/child-policies/{policyId}ipam:write⚑ ipamAutoAllocationDelete a child-prefix policy. Refuses while reservations from it are still live.GET /ipam/child-policies/{policyId}ipam:read⚑ ipamAutoAllocationGet one child-prefix policy.PATCH /ipam/child-policies/{policyId}ipam:write⚑ ipamAutoAllocationReplace a child-prefix policy. Send the whole policy, not just the changed fields.POST /ipam/child-policies/{policyId}/actions/reserveipam:write⚑ ipamAutoAllocationLease a child prefix (and a VLAN id when the policy has a pool) according to this policy. Repeat with the same idempotencyKey to get the same reservation back.GET /ipam/child-reservationsipam:read⚑ ipamAutoAllocationList the child-prefix reservations of this account, oldest first. Includes released rows.POST /ipam/child-reservations/{reservationId}/actions/set-stateipam:write⚑ ipamAutoAllocationMove a reservation to another state. Releasing from quarantine takes two steps: release_pending, then released.GET /ipam/groupsipam:readList the IPAM groups of this account, oldest first.POST /ipam/groups201ipam:writeCreate an IPAM group. Returns 201 with the group.DELETE /ipam/groups/{groupId}ipam:writeDelete an IPAM group. Refuses while prefixes, plans or plan placements still point at it.GET /ipam/groups/{groupId}ipam:readGet one IPAM group.PATCH /ipam/groups/{groupId}ipam:writeReplace an IPAM group. Send the whole group, not just the changed fields.GET /ipam/prefixesipam:readList the IPAM prefixes of this account, oldest first. Includes child blocks.POST /ipam/prefixes201ipam:writeCreate an IPAM prefix. Pass parentId to carve a child block out of an existing one. Returns 201.DELETE /ipam/prefixes/{prefixId}ipam:writeDelete an IPAM prefix. Refuses while child blocks, active allocations, plans or plan placements point at it. Also removes the released-allocation history of this block.GET /ipam/prefixes/{prefixId}ipam:readGet one IPAM prefix, with its address usage.PATCH /ipam/prefixes/{prefixId}ipam:writeReplace an IPAM prefix. Send the whole prefix; parentId is ignored (a block cannot move).GET /ipam/prefixes/{prefixId}/allocationsipam:readList the address allocations of one prefix, oldest first. Includes released rows unless status=active.POST /ipam/reservationsipam:writeReserve one or more addresses without a server. Already-taken addresses are skipped and returned by name.GET /ipam/vlan-poolsipam:read⚑ ipamAutoAllocation⚑ networkAutomationList the VLAN pools of this account, oldest first.POST /ipam/vlan-pools201ipam:write⚑ ipamAutoAllocation⚑ networkAutomationCreate a VLAN pool on a network fabric. Returns 201 with the pool.DELETE /ipam/vlan-pools/{poolId}ipam:write⚑ ipamAutoAllocation⚑ networkAutomationDelete a VLAN pool. Refuses while any reservation (including released ones) or policy points at it.GET /ipam/vlan-pools/{poolId}ipam:read⚑ ipamAutoAllocation⚑ networkAutomationGet one VLAN pool.PATCH /ipam/vlan-pools/{poolId}ipam:write⚑ ipamAutoAllocation⚑ networkAutomationReplace a VLAN pool. Send the whole pool. The fabric cannot change while reservations are live.GET /ipam/vlan-reservationsipam:read⚑ ipamAutoAllocation⚑ networkAutomationList the VLAN reservations of this account, oldest first. Includes released rows.POST /ipam/vlan-reservations/{reservationId}/actions/set-stateipam:write⚑ ipamAutoAllocation⚑ networkAutomationMove a reservation to another state. Releasing from quarantine takes two steps: release_pending, then released.
Plans
What your customers can order: VPS plans, hosting plans and game plans.
GET /plans/gamecatalog:readList the game plans of this account, oldest first.POST /plans/game201catalog:writeCreate a game plan. Returns 201 with the plan.GET /plans/game/{planId}catalog:readGet one game plan.PATCH /plans/game/{planId}catalog:writeReplace a game plan. Send the whole plan, not just the changed fields.POST /plans/game/{planId}/actions/{verb}catalog:writeActivate or deactivate a game plan. Verbs: activate, deactivate.GET /plans/vpscatalog:readList the VPS plans of this account, oldest first.POST /plans/vps201catalog:writeCreate a VPS plan. Returns 201 with the plan.GET /plans/vps/{planId}catalog:readGet one VPS plan.PATCH /plans/vps/{planId}catalog:writeReplace a VPS plan. Send the whole plan, not just the changed fields.POST /plans/vps/{planId}/actions/{verb}catalog:writeActivate or deactivate a VPS plan. Verbs: activate, deactivate.GET /plans/webcatalog:readList the hosting plans of this account, oldest first.POST /plans/web201catalog:writeCreate a hosting plan. Returns 201 with the plan.GET /plans/web/{planId}catalog:readGet one hosting plan.PATCH /plans/web/{planId}catalog:writeReplace a hosting plan. Send the whole plan, not just the changed fields.POST /plans/web/{planId}/actions/{verb}catalog:writeActivate or deactivate a hosting plan. Verbs: activate, deactivate.
Billing
Read-only: subscription, invoices, payments and credit.
GET /billing/creditbilling:readRead the prepaid credit balance of this account, in cents.GET /billing/credit/ledgerbilling:readList the credit ledger entries of this account, newest first.GET /billing/invoicesbilling:readList the billing documents of this account, newest first.GET /billing/invoices/{invoiceId}billing:readRead one billing document, including its line items.GET /billing/paymentsbilling:readList the payments of this account, newest first.GET /billing/subscriptionbilling:readRead the subscription of this account: status, capacity and monthly price.
Events
The audit log of this account. Page until nextCursor is null - a short page does not mean the end.
GET /eventsactivity:readList audit events of this account, newest first. Page until nextCursor is null; a short page does not mean the end.
Notifications
The inbox of the signed-in member, grouped per subject, plus which categories reach that member over which channel. App sessions only: a notification is addressed to a person, and an API key is a machine secret - use Events for machine access to what happened.
GET /notification-preferencesThe notification matrix of the signed-in member: per category, per channel, on or off.PUT /notification-preferencesSet part or all of the notification matrix in one call. Returns the matrix as it now stands.GET /notificationsList the notifications of the signed-in member, grouped per subject, newest first. Page until nextCursor is null.POST /notifications/{notificationId}/readMark one notification as read. Returns the new unread badge count.POST /notifications/read-allMark every unread notification of this member as read. Returns the new unread badge count.GET /notifications/unread-countThe unread badge count of this member: unread subjects, not rows.
Tickets
Support conversations with the Provibr desk. App sessions only: every message has an author, and an API key is a machine secret with no person behind it - use Events for machine access to what happened.
GET /ticketsList the support conversations of this account, most recent activity first. Page until nextCursor is null.POST /tickets201Open a support conversation. Returns the ticket including its first message.GET /tickets/{ticketId}One support conversation: the header and the whole thread, oldest message first.POST /tickets/{ticketId}/closeClose a support conversation. Returns the conversation as it now stands.POST /tickets/{ticketId}/messagesReply to a support conversation. Returns the conversation as it now stands.
Webhooks
Endpoints that receive lifecycle events, and the delivery history of each of them.
GET /webhook-deliveries/{deliveryId}webhooks:read⚑ lifecycleBindingsRead one webhook delivery, including every attempt that was made.POST /webhook-deliveries/{deliveryId}/actions/cancelwebhooks:write⚑ lifecycleBindingsCancel a delivery. Returns the state reached: cancelled (nothing was sent) or cancel_requested (a request is in flight and its remote effect is unknown).POST /webhook-deliveries/{deliveryId}/actions/resend202webhooks:write⚑ lifecycleBindingsSend an existing delivery again under a NEW delivery ID. The receiver will process the event a second time; this is not a retry.POST /webhook-deliveries/{deliveryId}/actions/retry202webhooks:write⚑ lifecycleBindingsRetry a failed delivery under the same delivery ID. The body and its signature stay byte-identical.GET /webhookswebhooks:read⚑ lifecycleBindingsList the webhook endpoints of this account, oldest first.POST /webhooks201webhooks:write⚑ lifecycleBindingsCreate a webhook endpoint. Returns 201 with the endpoint and the signing secret — shown once and never again.DELETE /webhooks/{webhookId}webhooks:write⚑ lifecycleBindingsDelete a webhook endpoint. Its lifecycle bindings AND its entire delivery history are deleted with it.GET /webhooks/{webhookId}webhooks:read⚑ lifecycleBindingsRead one webhook endpoint, including its full revision history.PATCH /webhooks/{webhookId}webhooks:write⚑ lifecycleBindingsPublish a new revision of a webhook endpoint. Omitted fields are carried over from the current revision.POST /webhooks/{webhookId}/actions/rotate-secretwebhooks:write⚑ lifecycleBindingsRotate the HMAC signing secret. Publishes a new revision and returns the secret once; earlier deliveries stay verifiable with their own key ID.POST /webhooks/{webhookId}/actions/test202webhooks:write⚑ lifecycleBindingsQueue a synthetic test delivery. Returns 202 with a deliveryId; nothing has been sent yet.GET /webhooks/{webhookId}/deliverieswebhooks:read⚑ lifecycleBindingsList the deliveries of one webhook endpoint, newest first.
Devices
The phones and tablets the Provibr app runs on. App sessions only, and a push token is never returned - registering a device is the opt-in for push, forgetting one is the way back.
GET /devicesList the devices of the signed-in member, active ones first, including disabled ones.POST /devicesRegister this device for push, or refresh the registration it already has.DELETE /devices/{deviceId}Stop sending push to one of your devices. Returns the device as it now stands.
La description lisible par une machine
La même information sous la forme d’un document OpenAPI 3.1, généré à partir du code qui sert ces endpoints plutôt qu’écrit à côté. Confiez-le à un générateur de clients, à un éditeur ou à une suite de tests.
curl -s https://app.provibr.com/api/v1/openapi.jsonAu sein de cette version, les changements sont des ajouts : nouveaux endpoints, nouveaux champs facultatifs, nouvelles valeurs dans une liste. Lisez avec indulgence les champs que vous ne connaissez pas, et ne traitez pas comme fatal un code d’erreur inconnu.