banshee is under active development: collectors will keep changing, and a version you publish will likely need adjustments. For stable use, wait for version 1.0.0.

Cloud

banshee/cloud-collection/cloud/ovh/https/

domains

The days before expiry, the renewal and the service status of each OVHcloud domain name of an account, by the OVHcloud API.

  • cloud
  • ovhcloud
  • ovh
  • domains
  • expiry
  • renewal
  • https
  • api

by banshee0 installations1 version

v1Latest

Published on October 27, 2026. First version.

Keen 1.0.0 · 77 lines
// The domain names of an OVHcloud account, by the OVHcloud API with an application's keys: for each domain, the
// days left before it expires, how it is renewed, and the status of its service.
// Reads GET <api>/domain, then for each domain GET <api>/domain/<name>/serviceInfos, each signed by ovh.sign.
need('protocol:http');
need('protocol:ovh');
$endpoint = need('protocol:ovh:endpoint');

per('domain', 'Domains');
output('days-left', 'number', array('label' => 'Before expiry', 'unit' => 'd', 'per' => 'domain'));
output('renewal', 'text', array('label' => 'Renewal', 'per' => 'domain'));
output('status', 'text', array('label' => 'Service status', 'per' => 'domain'));

watch.below('expiring', 'Domain expiring', 'days-left', array('warning' => 30, 'critical' => 7));
watch.state('not-renewed', 'Domain deleted at expiry', 'renewal', array(
    'help'    => 'Raises when the domain is set to be deleted at its expiry rather than renewed.',
    'warning' => array('delete-at-expiration'),
));
watch.state('service', 'Domain service unpaid or expired', 'status', array('critical' => array('expired', 'unPaid', 'pendingDebt')));
watch.missing('missing', 'Domain missing', 'days-left', array('enabled' => false));

// The address of the API set on the probe, checked: https:// only, its host and its version, without a final
// slash, https://eu.api.ovh.com/1.0. The signed headers go there and nowhere else.
function ovh_api($endpoint) {
    if (list.count(text.match($endpoint, '^https://[A-Za-z0-9.-]+/[0-9.]+$')) == 0) {
        fail('misconfigured', 'The address of the OVHcloud API is not written https://<host>/<version>',
            'For instance https://eu.api.ovh.com/1.0, https://ca.api.ovh.com/1.0 or https://api.us.ovhcloud.com/1.0.');
    }
    return $endpoint;
}

// One signed GET on the OVHcloud API: ovh.sign gives the headers that carry the application key, the consumer
// key and the signature; the application secret never leaves.
function ovh_get($api, $path, $what) {
    $url = $api . $path;
    $answer = http.get($url, headers=ovh.sign('GET', $url, ''));
    if ($answer['status'] == 401) {
        fail('misconfigured', 'OVHcloud refuses the keys, code 401',
            'The application key, its secret or the consumer key is wrong, expired or revoked, or the clock of the poller is off.');
    }
    if ($answer['status'] == 403) {
        fail('misconfigured', 'The consumer key may not read ' . $what . ', code 403',
            'Its access rules must allow GET on the routes the documentation of the collector names.');
    }
    if ($answer['status'] == 400 || $answer['status'] == 404) {
        fail('misconfigured', 'OVHcloud refuses to read ' . $what . ', code ' . $answer['status']);
    }
    if ($answer['status'] != 200) {
        fail('unreachable', 'OVHcloud answers the reading of ' . $what . ' with code ' . $answer['status']);
    }
    return json.read($answer['body']);
}

$api = ovh_api($endpoint);

// ["example.com", …], then for each domain {"expiration": "2027-03-14", "status": "ok", "renew": {"automatic":
// true, "deleteAtExpiration": false, "forced": false, "manualPayment": false, "period": 12}, …}. expiration is a
// date without a time: it is read as midnight UTC. status is autorenewInProgress, expired, inCreation, ok,
// pendingDebt or unPaid. renew may be null, read as '': the renewal is then unknown.
$now = time.now();
foreach (ovh_get($api, '/domain', 'the domains') as $name) {
    $service = ovh_get($api, '/domain/' . url.encode($name) . '/serviceInfos', 'the service of ' . $name);
    $expiration = time.parse($service['expiration'] . 'T00:00:00Z', 'rfc3339');
    value('days-left', number.round(($expiration - $now) / 86400, 1), $name);
    $renew = list.get($service, 'renew', '');
    $renewal = 'unknown';
    if (list.is($renew)) {
        if (list.get($renew, 'deleteAtExpiration', false)) {
            $renewal = 'delete-at-expiration';
        } elseif (list.get($renew, 'automatic', false)) {
            $renewal = 'automatic';
        } else {
            $renewal = 'manual';
        }
    }
    value('renewal', $renewal, $name);
    value('status', $service['status'], $name);
}