Skip to content

Certificates ​

TLS certificates InfraNest tracks or issues, and when they expire.

Common tasks

Only in the app: Ordering and renewing certificates.

How it works in the app: help article.

Scopes: Read with domains.read or monitoring.read, change with monitoring.write. See Tokens and scopes.

Every endpoint below takes a token in the Authorization: Bearer header and answers JSON. Errors use the shared format in Errors. Base address: https://api.infranest.io/api.

Certificates ​

List certificates ​

Every TLS certificate in the organization, expired and expiring first, each with its health, findings and who renews it. Narrow it with domain_id or monitoring_check_id; the list is not paginated.

GET/organizations/{organization}/certificates
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
[
  {
    "id": 10,
    "organization_id": 1,
    "source": "manual",
    "monitoring_check_id": null,
    "domain_id": null,
    "cloud_server_id": null,
    "provider_credential_id": null,
    "fingerprint": "61e58004edb797c08c1261beb441b56e31cc903bde63441cd503dca9aa79f099",
    "subject": "vpn.northwind.example",
    "issuer": "vpn.northwind.example",
    "serial": "EAABC774",
    "sans": [
      "vpn.northwind.example"
    ],
    "not_before": "2025-01-03T12:00:00.000000Z",
    "not_after": "2026-01-03T12:00:00.000000Z",
    "purpose": "tls_server",
    "key_algo": "rsa",
    "key_bits": 4096,
    "sig_algo": "SHA256-RSA",
    "trust": "self_signed",
    "validation": "OV",
    "is_wildcard": false,
    "is_multi_san": false,
    "managed": false,
    "renewal_source": null,
    "classification_override": null,
    "status": "expired",
    "superseded_by_id": null,
    "dismissed_at": null,
    "deployment_locations": [],
    "owner": null,
    "notes": null,
    "first_seen_at": "2025-11-16T12:00:00.000000Z",
    "last_seen_at": "2026-01-15T06:00:00.000000Z",
    "created_at": "2026-01-15T12:00:00.000000Z",
    "updated_at": "2026-01-15T12:00:00.000000Z",
    "subject_org": null,
    "subject_location": null,
    "chain": null,
    "chain_status": null,
    "sources": [
      {
        "source": "manual",
        "last_seen": "2026-01-15T06:00:00+00:00",
        "first_seen": "2025-11-16T12:00:00+00:00"
      }
    ],
    "crl_url": null,
    "ocsp_url": null,
    "caa_status": null,
    "caissuer_url": null,
    "no_auto_merge": false,
    "price_yearly": "0.00",
    "price_source": "manual",
    "price_currency": "EUR",
    "billing_interval": "yearly",
    "interval_count": 1,
    "renewal_type": "internal",
    "renewal_type_basis": "guess",
    "served_status": null,
    "served_checked_at": null,
    "subscription_ends_at": null,
    "seller": null,
    "role": null,
    "ct_finding": null,
    "owner_user_id": null,
    "custom_fields": [],
    "renewer": null,
    "findings": [
      {
        "tier": "problem",
        "code": "expired",
        "severity": "critical",
        "subject": {
          "type": "certificate",
          "id": 10,
          "name": "vpn.northwind.example"
        },
        "certificate_ids": [
          10
        ],
        "addresses": [],
        "attention_key": "certificate:10",
        "due_at": "2026-01-03T12:00:00+00:00",
        "evidence": {
          "expires_at": "2026-01-03T12:00:00+00:00",
          "days_left": -12,
          "renews_itself": false,
          "renewal_type": "internal",
          "renewal_type_basis": "guess",
          "renewal_source": null,
          "issuer": "vpn.northwind.example",
          "served_status": null,
          "role": null,
          "renewer": null
        },
        "copy": {
          "title": {
            "key": "certificates.rules.expired.title",
            "params": {
              "expires_at": "2026-01-03T12:00:00+00:00",
              "name": "vpn.northwind.example"
            }
          },
          "detail": {
            "key": "certificates.rules.expired.detail",
            "params": {
              "expires_at": "2026-01-03T12:00:00+00:00",
              "name": "vpn.northwind.example"
            }
          },
          "short": {
            "key": "certificates.rules.expired.short",
            "params": {
              "expires_at": "2026-01-03T12:00:00+00:00",
              "name": "vpn.northwind.example"
            }
          }
        },
        "fix": {
          "kind": "route",
          "label": "certificates.rules.expired.fix",
          "route": "/certificates/10"
        },
        "dismissible": false,
        "advisor": null,
        "decision": null,
        "automation": null,
        "sources": [],
        "also": []
      }
    ],
    "finding": {
      "tier": "problem",
      "code": "expired",
      "severity": "critical",
      "subject": {
        "type": "certificate",
        "id": 10,
        "name": "vpn.northwind.example"
      },
      "certificate_ids": [
        10
      ],
      "addresses": [],
      "attention_key": "certificate:10",
      "due_at": "2026-01-03T12:00:00+00:00",
      "evidence": {
        "expires_at": "2026-01-03T12:00:00+00:00",
        "days_left": -12,
        "renews_itself": false,
        "renewal_type": "internal",
        "renewal_type_basis": "guess",
        "renewal_source": null,
        "issuer": "vpn.northwind.example",
        "served_status": null,
        "role": null,
        "renewer": null
      },
      "copy": {
        "title": {
          "key": "certificates.rules.expired.title",
          "params": {
            "expires_at": "2026-01-03T12:00:00+00:00",
            "name": "vpn.northwind.example"
          }
        },
        "detail": {
          "key": "certificates.rules.expired.detail",
          "params": {
            "expires_at": "2026-01-03T12:00:00+00:00",
            "name": "vpn.northwind.example"
          }
        },
        "short": {
          "key": "certificates.rules.expired.short",
          "params": {
            "expires_at": "2026-01-03T12:00:00+00:00",
            "name": "vpn.northwind.example"
          }
        }
      },
      "fix": {
        "kind": "route",
        "label": "certificates.rules.expired.fix",
        "route": "/certificates/10"
      },
      "dismissible": false,
      "advisor": null,
      "decision": null,
      "automation": null,
      "sources": [],
      "also": []
    },
    "health": {
      "grade": "warning",
      "letter": null,
      "issues": [
        {
          "code": "self_signed",
          "severity": "warning"
        }
      ]
    },
    "in_use": false,
    "tags": [],
    "check": null,
    "domain": null
  },
  {
    "id": 2,
    "organization_id": 1,
    "source": "probe",
    "monitoring_check_id": 2,
    "domain_id": 3,
    "cloud_server_id": null,
    "provider_credential_id": null,
    "fingerprint": "b6b045177e3469ad718546c6233a2b7f0166d10e466deeaa6d16f6244f6654db",
    "subject": "northwind-app.dev",
    "issuer": "Let's Encrypt (R11)",
    "serial": "B4B32EC4",
    "sans": [
      "northwind-app.dev",
      "*.northwind-app.dev"
    ],
    "not_before": "2025-11-07T12:00:00.000000Z",
    "not_after": "2026-02-05T12:00:00.000000Z",
    "purpose": "tls_server",
    "key_algo": "ecdsa",
    "key_bits": 256,
    "sig_algo": "ECDSA-SHA256",
    "trust": "public",
    "validation": "DV",
    "is_wildcard": true,
    "is_multi_san": true,
    "managed": true,
    "renewal_source": "Let's Encrypt (R11)",
    "classification_override": null,
    "status": "expiring_soon",
    "superseded_by_id": null,
    "dismissed_at": null,
    "deployment_locations": [
      {
        "endpoint": "northwind-app.dev:443",
        "last_seen": "2026-01-15T11:50:00+00:00",
        "first_seen": "2026-01-08T12:00:00+00:00"
      }
    ],
    "owner": null,
    "notes": null,
    "first_seen_at": "2025-11-16T12:00:00.000000Z",
    "last_seen_at": "2026-01-15T06:00:00.000000Z",
    "created_at": "2026-01-15T12:00:00.000000Z",
    "updated_at": "2026-01-15T12:00:00.000000Z",
    "subject_org": null,
    "subject_location": null,
    "chain": [
      {
        "subject": "northwind-app.dev",
        "issuer": "Let's Encrypt (R11)",
        "is_ca": false,
        "self_signed": false,
        "not_after": "2026-02-05T12:00:00+00:00",
        "fingerprint": "b6b045177e3469ad718546c6233a2b7f0166d10e466deeaa6d16f6244f6654db"
      },
      {
        "subject": "Let's Encrypt (R11)",
        "issuer": "ISRG Root X1",
        "is_ca": true,
        "self_signed": false,
        "not_after": "2030-01-15T12:00:00+00:00",
        "fingerprint": "c12c8a7dcb3f10b2cfd6efb6d14e6516f76cb6d3537ffae6a1f804b618d0cf3c"
      }
    ],
    "chain_status": "trusted",
    "sources": [
      {
        "source": "probe",
        "last_seen": "2026-01-15T06:00:00+00:00",
        "first_seen": "2025-11-16T12:00:00+00:00"
      }
    ],
    "crl_url": null,
    "ocsp_url": null,
    "caa_status": null,
    "caissuer_url": "http://r11.i.lencr.org/",
    "no_auto_merge": false,
    "price_yearly": "0.00",
    "price_source": "manual",
    "price_currency": "EUR",
    "billing_interval": "yearly",
    "interval_count": 1,
    "renewal_type": "automatic",
    "renewal_type_basis": "guess",
    "served_status": "served",
    "served_checked_at": "2026-01-15T11:50:00.000000Z",
    "subscription_ends_at": null,
    "seller": null,
    "role": null,
    "ct_finding": null,
    "owner_user_id": null,
    "custom_fields": [],
    "renewer": null,
    "findings": [
      {
        "tier": "problem",
        "code": "renewal_overdue",
        "severity": "warning",
        "subject": {
          "type": "certificate",
          "id": 2,
          "name": "northwind-app.dev"
        },
        "certificate_ids": [
          2
        ],
        "addresses": [
          "northwind-app.dev:443"
        ],
        "attention_key": "certificate_renewal:2",
        "due_at": "2026-02-05T12:00:00+00:00",
        "evidence": {
          "expires_at": "2026-02-05T12:00:00+00:00",
          "days_left": 21,
          "renews_itself": true,
          "renewal_type": "automatic",
          "renewal_type_basis": "guess",
          "renewal_source": "Let's Encrypt (R11)",
          "issuer": "Let's Encrypt (R11)",
          "served_status": "served",
          "role": null,
          "renewer": null,
          "renews_around_at": "2026-01-06T00:00:00+00:00",
          "last_seen_at": "2026-01-15T06:00:00+00:00"
        },
        "copy": {
          "title": {
            "key": "certificates.rules.renewal_overdue.title",
            "params": {
              "renews_at": "2026-01-06T00:00:00+00:00",
              "expires_at": "2026-02-05T12:00:00+00:00",
              "n": 9,
              "name": "northwind-app.dev"
            }
          },
          "detail": {
            "key": "certificates.rules.renewal_overdue.detail",
            "params": {
              "renews_at": "2026-01-06T00:00:00+00:00",
              "expires_at": "2026-02-05T12:00:00+00:00",
              "n": 9,
              "name": "northwind-app.dev"
            }
          },
          "short": {
            "key": "certificates.rules.renewal_overdue.short",
            "params": {
              "renews_at": "2026-01-06T00:00:00+00:00",
              "expires_at": "2026-02-05T12:00:00+00:00",
              "n": 9,
              "name": "northwind-app.dev"
            }
          }
        },
        "fix": {
          "kind": "route",
          "label": "certificates.rules.renewal_overdue.fix",
          "route": "/certificates/2"
        },
        "dismissible": true,
        "advisor": null,
        "decision": null,
        "automation": null,
        "sources": [],
        "also": []
      }
    ],
    "finding": {
      "tier": "problem",
      "code": "renewal_overdue",
      "severity": "warning",
      "subject": {
        "type": "certificate",
        "id": 2,
        "name": "northwind-app.dev"
      },
      "certificate_ids": [
        2
      ],
      "addresses": [
        "northwind-app.dev:443"
      ],
      "attention_key": "certificate_renewal:2",
      "due_at": "2026-02-05T12:00:00+00:00",
      "evidence": {
        "expires_at": "2026-02-05T12:00:00+00:00",
        "days_left": 21,
        "renews_itself": true,
        "renewal_type": "automatic",
        "renewal_type_basis": "guess",
        "renewal_source": "Let's Encrypt (R11)",
        "issuer": "Let's Encrypt (R11)",
        "served_status": "served",
        "role": null,
        "renewer": null,
        "renews_around_at": "2026-01-06T00:00:00+00:00",
        "last_seen_at": "2026-01-15T06:00:00+00:00"
      },
      "copy": {
        "title": {
          "key": "certificates.rules.renewal_overdue.title",
          "params": {
            "renews_at": "2026-01-06T00:00:00+00:00",
            "expires_at": "2026-02-05T12:00:00+00:00",
            "n": 9,
            "name": "northwind-app.dev"
          }
        },
        "detail": {
          "key": "certificates.rules.renewal_overdue.detail",
          "params": {
            "renews_at": "2026-01-06T00:00:00+00:00",
            "expires_at": "2026-02-05T12:00:00+00:00",
            "n": 9,
            "name": "northwind-app.dev"
          }
        },
        "short": {
          "key": "certificates.rules.renewal_overdue.short",
          "params": {
            "renews_at": "2026-01-06T00:00:00+00:00",
            "expires_at": "2026-02-05T12:00:00+00:00",
            "n": 9,
            "name": "northwind-app.dev"
          }
        }
      },
      "fix": {
        "kind": "route",
        "label": "certificates.rules.renewal_overdue.fix",
        "route": "/certificates/2"
      },
      "dismissible": true,
      "advisor": null,
      "decision": null,
      "automation": null,
      "sources": [],
      "also": []
    },
    "health": {
      "grade": "good",
      "letter": "A",
      "issues": []
    },
    "in_use": true,
    "tags": [
      {
        "id": 6,
        "name": "Northwind Platform",
        "color": "#14b8a6",
        "pivot": {
          "taggable_type": "App\\Models\\SslCertificate",
          "taggable_id": 2,
          "tag_id": 6
        }
      }
    ],
    "check": {
      "id": 2,
      "name": "App — northwind-app.dev",
      "target": "https://northwind-app.dev",
      "interval_minutes": 0
    },
    "domain": {
      "id": 3,
      "name": "northwind-app",
      "tld": "dev",
      "full_name": "northwind-app.dev",
      "archivable": true,
      "dns_host": null,
      "management": "tracked",
      "sync_halt": null
    }
  }
]
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


Get a certificate ​

One certificate with its domain, server, monitor, owner and tags, where it is served and its place in the renewal series. Also includes its findings and whether its files can be fetched from the provider.

GET/organizations/{organization}/certificates/{sslCertificate}
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

sslCertificateintegerrequired

The SSL certificate's ID.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/1" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/1" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/1', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/1",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/1', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/1", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "id": 1,
  "organization_id": 1,
  "source": "probe",
  "monitoring_check_id": 1,
  "domain_id": 1,
  "cloud_server_id": null,
  "provider_credential_id": null,
  "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9",
  "subject": "northwind.com",
  "issuer": "Let's Encrypt (R3)",
  "serial": "2395BA42",
  "sans": [
    "northwind.com",
    "www.northwind.com"
  ],
  "not_before": "2025-12-30T12:00:00.000000Z",
  "not_after": "2026-03-30T12:00:00.000000Z",
  "purpose": "tls_server",
  "key_algo": "rsa",
  "key_bits": 2048,
  "sig_algo": "SHA256-RSA",
  "trust": "public",
  "validation": "DV",
  "is_wildcard": false,
  "is_multi_san": true,
  "managed": true,
  "renewal_source": "Let's Encrypt (R3)",
  "classification_override": null,
  "status": "valid",
  "superseded_by_id": null,
  "dismissed_at": null,
  "deployment_locations": [
    {
      "endpoint": "northwind.com:443",
      "last_seen": "2026-01-15T11:50:00+00:00",
      "first_seen": "2026-01-08T12:00:00+00:00"
    }
  ],
  "owner": null,
  "notes": null,
  "first_seen_at": "2025-11-16T12:00:00.000000Z",
  "last_seen_at": "2026-01-15T06:00:00.000000Z",
  "created_at": "2026-01-15T12:00:00.000000Z",
  "updated_at": "2026-01-15T12:00:00.000000Z",
  "subject_org": null,
  "subject_location": null,
  "chain": [
    {
      "subject": "northwind.com",
      "issuer": "Let's Encrypt (R3)",
      "is_ca": false,
      "self_signed": false,
      "not_after": "2026-03-30T12:00:00+00:00",
      "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9"
    },
    {
      "subject": "Let's Encrypt (R3)",
      "issuer": "ISRG Root X1",
      "is_ca": true,
      "self_signed": false,
      "not_after": "2030-01-15T12:00:00+00:00",
      "fingerprint": "e04ca19ed66485fb479283c98fbfb4cda1ab67de1965304902bec3f6a55a2c8e"
    }
  ],
  "chain_status": "trusted",
  "sources": [
    {
      "source": "probe",
      "last_seen": "2026-01-15T06:00:00+00:00",
      "first_seen": "2025-11-16T12:00:00+00:00"
    }
  ],
  "crl_url": null,
  "ocsp_url": null,
  "caa_status": "ok",
  "caissuer_url": "http://r3.i.lencr.org/",
  "no_auto_merge": false,
  "price_yearly": "0.00",
  "price_source": "manual",
  "price_currency": "EUR",
  "billing_interval": "yearly",
  "interval_count": 1,
  "renewal_type": "automatic",
  "renewal_type_basis": "guess",
  "served_status": "served",
  "served_checked_at": "2026-01-15T11:50:00.000000Z",
  "subscription_ends_at": null,
  "seller": null,
  "role": null,
  "ct_finding": null,
  "owner_user_id": null,
  "health": {
    "grade": "good",
    "letter": "A",
    "issues": []
  },
  "in_use": true,
  "tags": [
    {
      "id": 5,
      "name": "Northwind Retail",
      "color": "#0ea5e9",
      "pivot": {
        "taggable_type": "App\\Models\\SslCertificate",
        "taggable_id": 1,
        "tag_id": 5
      }
    }
  ],
  "check": {
    "id": 1,
    "name": "Marketing site",
    "target": "https://northwind.com",
    "interval_minutes": 0
  },
  "domain": {
    "id": 1,
    "name": "northwind",
    "tld": "com",
    "full_name": "northwind.com",
    "archivable": true,
    "dns_host": null,
    "management": "tracked",
    "sync_halt": null
  },
  "cloud_server": null,
  "provider_credential": null,
  "superseded_by": null,
  "owner_user": null,
  "owner_candidates": [
    {
      "id": 1,
      "name": "Alex Rivera"
    },
    {
      "id": 3,
      "name": "Priya Raghunathan"
    }
  ],
  "bundle_available": false,
  "whereabouts": {
    "role": "current",
    "current_certificate": null,
    "renewer": null,
    "served_on": [
      {
        "endpoint_id": 1,
        "host": "northwind.com",
        "port": 443,
        "as": "edge",
        "proxied": true,
        "last_checked_at": "2026-01-15T11:50:00+00:00",
        "last_check_source": "scan",
        "zone": {
          "id": 1,
          "name": "northwind.com",
          "domain_id": 1
        },
        "dns_record": {
          "id": 1,
          "type": "A",
          "name": "@",
          "value": "198.51.100.10"
        },
        "cloud_server": {
          "id": 1,
          "name": "web-01"
        },
        "monitor": {
          "id": 1,
          "name": "Marketing site"
        },
        "renewer": null
      }
    ],
    "lifecycle": {
      "previous": null,
      "next": null,
      "replaced_at": null,
      "renewals": 0,
      "lead_days": null,
      "expected_renewal_at": "2026-02-28T12:00:00+00:00"
    },
    "rollout": null
  },
  "findings": []
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


Update a certificate ​

Changes InfraNest’s record of the certificate: owner, notes, subscription end date, seller, tags and any corrections to how it is classified (purpose, trust, validation, managed, renewal type). Only the fields you send change; owner_user_id must be a member of the organization.

PATCH/organizations/{organization}/certificates/{sslCertificate}
Scope monitoring.writeChanges data
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

sslCertificateintegerrequired

The SSL certificate's ID.

Bodyapplication/json
ownerstring, nullableoptional

Must not be greater than 255 characters.

owner_user_idinteger, nullableoptional

A member who owns it; checked against the organization below.

notesstring, nullableoptional

Must not be greater than 2000 characters.

subscription_ends_atstring, nullableoptional

A paid certificate's subscription and who sold it. Must be a valid date.

sellerstring, nullableoptional

Must not be greater than 255 characters.

classification_overrideobject, nullableoptional
tag_idsarrayoptional

Request

sh
curl -X PATCH "https://api.infranest.io/api/organizations/42/certificates/1" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"owner":"Platform team","notes":"Renewed automatically by the load balancer."}'
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
$body = @'
{
  "owner": "Platform team",
  "notes": "Renewed automatically by the load balancer."
}
'@
Invoke-RestMethod -Method Patch -Uri "https://api.infranest.io/api/organizations/42/certificates/1" -Headers $headers -ContentType "application/json" -Body $body
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('PATCH', 'https://api.infranest.io/api/organizations/42/certificates/1', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
    'json' => [
        'owner' => 'Platform team',
        'notes' => 'Renewed automatically by the load balancer.',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.patch(
    "https://api.infranest.io/api/organizations/42/certificates/1",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
    json={
        "owner": "Platform team",
        "notes": "Renewed automatically by the load balancer.",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/1', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "owner": "Platform team",
    "notes": "Renewed automatically by the load balancer."
  }),
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	req, err := http.NewRequest("PATCH", "https://api.infranest.io/api/organizations/42/certificates/1", strings.NewReader(`{"owner":"Platform team","notes":"Renewed automatically by the load balancer."}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "id": 1,
  "organization_id": 1,
  "source": "probe",
  "monitoring_check_id": 1,
  "domain_id": 1,
  "cloud_server_id": null,
  "provider_credential_id": null,
  "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9",
  "subject": "northwind.com",
  "issuer": "Let's Encrypt (R3)",
  "serial": "2395BA42",
  "sans": [
    "northwind.com",
    "www.northwind.com"
  ],
  "not_before": "2025-12-30T12:00:00.000000Z",
  "not_after": "2026-03-30T12:00:00.000000Z",
  "purpose": "tls_server",
  "key_algo": "rsa",
  "key_bits": 2048,
  "sig_algo": "SHA256-RSA",
  "trust": "public",
  "validation": "DV",
  "is_wildcard": false,
  "is_multi_san": true,
  "managed": true,
  "renewal_source": "Let's Encrypt (R3)",
  "classification_override": null,
  "status": "valid",
  "superseded_by_id": null,
  "dismissed_at": null,
  "deployment_locations": [
    {
      "endpoint": "northwind.com:443",
      "last_seen": "2026-01-15T11:50:00+00:00",
      "first_seen": "2026-01-08T12:00:00+00:00"
    }
  ],
  "owner": "Platform team",
  "notes": "Renewed automatically by the load balancer.",
  "first_seen_at": "2025-11-16T12:00:00.000000Z",
  "last_seen_at": "2026-01-15T06:00:00.000000Z",
  "created_at": "2026-01-15T12:00:00.000000Z",
  "updated_at": "2026-01-15T12:00:00.000000Z",
  "subject_org": null,
  "subject_location": null,
  "chain": [
    {
      "subject": "northwind.com",
      "issuer": "Let's Encrypt (R3)",
      "is_ca": false,
      "self_signed": false,
      "not_after": "2026-03-30T12:00:00+00:00",
      "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9"
    },
    {
      "subject": "Let's Encrypt (R3)",
      "issuer": "ISRG Root X1",
      "is_ca": true,
      "self_signed": false,
      "not_after": "2030-01-15T12:00:00+00:00",
      "fingerprint": "e04ca19ed66485fb479283c98fbfb4cda1ab67de1965304902bec3f6a55a2c8e"
    }
  ],
  "chain_status": "trusted",
  "sources": [
    {
      "source": "probe",
      "last_seen": "2026-01-15T06:00:00+00:00",
      "first_seen": "2025-11-16T12:00:00+00:00"
    }
  ],
  "crl_url": null,
  "ocsp_url": null,
  "caa_status": "ok",
  "caissuer_url": "http://r3.i.lencr.org/",
  "no_auto_merge": false,
  "price_yearly": "0.00",
  "price_source": "manual",
  "price_currency": "EUR",
  "billing_interval": "yearly",
  "interval_count": 1,
  "renewal_type": "automatic",
  "renewal_type_basis": "guess",
  "served_status": "served",
  "served_checked_at": "2026-01-15T11:50:00.000000Z",
  "subscription_ends_at": null,
  "seller": null,
  "role": null,
  "ct_finding": null,
  "owner_user_id": null,
  "tags": [
    {
      "id": 5,
      "name": "Northwind Retail",
      "color": "#0ea5e9",
      "pivot": {
        "taggable_type": "App\\Models\\SslCertificate",
        "taggable_id": 1,
        "tag_id": 5
      }
    }
  ],
  "owner_user": null
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.write scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.write",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "validation_failed",
  "message": "The owner field is required.",
  "hint": "Fix the fields listed in \"errors\" and send the request again.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#validation_failed",
  "errors": {
    "owner": [
      "The owner field is required."
    ]
  },
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
422 validation_failed
429 rate_limited


Get a certificate’s files from the provider ​

Asks the provider the certificate came from for its files and returns the files; each fetch is recorded in your audit log. Requires write access, and is refused when the certificate did not come from a connected provider that can hand files back or is not linked to a domain.

GET/organizations/{organization}/certificates/{sslCertificate}/bundle
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

sslCertificateintegerrequired

The SSL certificate's ID.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/1/bundle" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/1/bundle" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/1/bundle', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/1/bundle",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/1/bundle', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/1/bundle", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited

No success response is recorded for this endpoint yet.


Preview the CAA fix for a certificate ​

The exact CAA record that would let this certificate’s authority issue it again, the DNS zone it would go in, the CAA records already there and any other certificate it would block. Refused when the certificate’s CAA policy does not block its renewal; nothing is changed.

GET/organizations/{organization}/certificates/{sslCertificate}/caa-fix
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

sslCertificateintegerrequired

The SSL certificate's ID.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/1/caa-fix" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/1/caa-fix" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/1/caa-fix', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/1/caa-fix",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/1/caa-fix', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/1/caa-fix", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited

No success response is recorded for this endpoint yet.


Dismiss or restore a certificate ​

Sets the certificate aside so it no longer counts towards domain coverage. Send dismissed: false to restore it.

POST/organizations/{organization}/certificates/{sslCertificate}/dismiss
Scope monitoring.writeChanges data
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

sslCertificateintegerrequired

The SSL certificate's ID.

Request

sh
curl -X POST "https://api.infranest.io/api/organizations/42/certificates/1/dismiss" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Post -Uri "https://api.infranest.io/api/organizations/42/certificates/1/dismiss" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.infranest.io/api/organizations/42/certificates/1/dismiss', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.post(
    "https://api.infranest.io/api/organizations/42/certificates/1/dismiss",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/1/dismiss', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("POST", "https://api.infranest.io/api/organizations/42/certificates/1/dismiss", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "id": 1,
  "organization_id": 1,
  "source": "probe",
  "monitoring_check_id": 1,
  "domain_id": 1,
  "cloud_server_id": null,
  "provider_credential_id": null,
  "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9",
  "subject": "northwind.com",
  "issuer": "Let's Encrypt (R3)",
  "serial": "2395BA42",
  "sans": [
    "northwind.com",
    "www.northwind.com"
  ],
  "not_before": "2025-12-30T12:00:00.000000Z",
  "not_after": "2026-03-30T12:00:00.000000Z",
  "purpose": "tls_server",
  "key_algo": "rsa",
  "key_bits": 2048,
  "sig_algo": "SHA256-RSA",
  "trust": "public",
  "validation": "DV",
  "is_wildcard": false,
  "is_multi_san": true,
  "managed": true,
  "renewal_source": "Let's Encrypt (R3)",
  "classification_override": null,
  "status": "valid",
  "superseded_by_id": null,
  "dismissed_at": "2026-01-15T12:00:00.000000Z",
  "deployment_locations": [
    {
      "endpoint": "northwind.com:443",
      "last_seen": "2026-01-15T11:50:00+00:00",
      "first_seen": "2026-01-08T12:00:00+00:00"
    }
  ],
  "owner": null,
  "notes": null,
  "first_seen_at": "2025-11-16T12:00:00.000000Z",
  "last_seen_at": "2026-01-15T06:00:00.000000Z",
  "created_at": "2026-01-15T12:00:00.000000Z",
  "updated_at": "2026-01-15T12:00:00.000000Z",
  "subject_org": null,
  "subject_location": null,
  "chain": [
    {
      "subject": "northwind.com",
      "issuer": "Let's Encrypt (R3)",
      "is_ca": false,
      "self_signed": false,
      "not_after": "2026-03-30T12:00:00+00:00",
      "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9"
    },
    {
      "subject": "Let's Encrypt (R3)",
      "issuer": "ISRG Root X1",
      "is_ca": true,
      "self_signed": false,
      "not_after": "2030-01-15T12:00:00+00:00",
      "fingerprint": "e04ca19ed66485fb479283c98fbfb4cda1ab67de1965304902bec3f6a55a2c8e"
    }
  ],
  "chain_status": "trusted",
  "sources": [
    {
      "source": "probe",
      "last_seen": "2026-01-15T06:00:00+00:00",
      "first_seen": "2025-11-16T12:00:00+00:00"
    }
  ],
  "crl_url": null,
  "ocsp_url": null,
  "caa_status": "ok",
  "caissuer_url": "http://r3.i.lencr.org/",
  "no_auto_merge": false,
  "price_yearly": "0.00",
  "price_source": "manual",
  "price_currency": "EUR",
  "billing_interval": "yearly",
  "interval_count": 1,
  "renewal_type": "automatic",
  "renewal_type_basis": "guess",
  "served_status": "served",
  "served_checked_at": "2026-01-15T11:50:00.000000Z",
  "subscription_ends_at": null,
  "seller": null,
  "role": null,
  "ct_finding": null,
  "owner_user_id": null,
  "tags": [
    {
      "id": 5,
      "name": "Northwind Retail",
      "color": "#0ea5e9",
      "pivot": {
        "taggable_type": "App\\Models\\SslCertificate",
        "taggable_id": 1,
        "tag_id": 5
      }
    }
  ]
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.write scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.write",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "idempotency_in_progress",
  "message": "A request with this Idempotency-Key is still being handled.",
  "hint": "Wait a few seconds, then send the same request with the same key again to get its result.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#idempotency_in_progress",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "validation_failed",
  "message": "The name field is required.",
  "hint": "Fix the fields listed in \"errors\" and send the request again.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#validation_failed",
  "errors": {
    "name": [
      "The name field is required."
    ]
  },
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
409 idempotency_in_progress
422 validation_failed idempotency_key_reused
429 rate_limited


Set a certificate’s price ​

Sets the yearly price InfraNest counts for this certificate, with optional currency and billing interval. Send an empty price to go back to the provider’s figure.

PUT/organizations/{organization}/certificates/{sslCertificate}/pricing
Scope monitoring.writeChanges data
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

sslCertificateintegerrequired

The SSL certificate's ID.

Request

sh
curl -X PUT "https://api.infranest.io/api/organizations/42/certificates/1/pricing" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"price_yearly":59,"currency":"EUR"}'
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
$body = @'
{
  "price_yearly": 59,
  "currency": "EUR"
}
'@
Invoke-RestMethod -Method Put -Uri "https://api.infranest.io/api/organizations/42/certificates/1/pricing" -Headers $headers -ContentType "application/json" -Body $body
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('PUT', 'https://api.infranest.io/api/organizations/42/certificates/1/pricing', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
    'json' => [
        'price_yearly' => 59,
        'currency' => 'EUR',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.put(
    "https://api.infranest.io/api/organizations/42/certificates/1/pricing",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
    json={
        "price_yearly": 59,
        "currency": "EUR",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/1/pricing', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "price_yearly": 59,
    "currency": "EUR"
  }),
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	req, err := http.NewRequest("PUT", "https://api.infranest.io/api/organizations/42/certificates/1/pricing", strings.NewReader(`{"price_yearly":59,"currency":"EUR"}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "id": 1,
  "organization_id": 1,
  "source": "probe",
  "monitoring_check_id": 1,
  "domain_id": 1,
  "cloud_server_id": null,
  "provider_credential_id": null,
  "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9",
  "subject": "northwind.com",
  "issuer": "Let's Encrypt (R3)",
  "serial": "2395BA42",
  "sans": [
    "northwind.com",
    "www.northwind.com"
  ],
  "not_before": "2025-12-30T12:00:00.000000Z",
  "not_after": "2026-03-30T12:00:00.000000Z",
  "purpose": "tls_server",
  "key_algo": "rsa",
  "key_bits": 2048,
  "sig_algo": "SHA256-RSA",
  "trust": "public",
  "validation": "DV",
  "is_wildcard": false,
  "is_multi_san": true,
  "managed": true,
  "renewal_source": "Let's Encrypt (R3)",
  "classification_override": null,
  "status": "valid",
  "superseded_by_id": null,
  "dismissed_at": null,
  "deployment_locations": [
    {
      "endpoint": "northwind.com:443",
      "last_seen": "2026-01-15T11:50:00+00:00",
      "first_seen": "2026-01-08T12:00:00+00:00"
    }
  ],
  "owner": null,
  "notes": null,
  "first_seen_at": "2025-11-16T12:00:00.000000Z",
  "last_seen_at": "2026-01-15T06:00:00.000000Z",
  "created_at": "2026-01-15T12:00:00.000000Z",
  "updated_at": "2026-01-15T12:00:00.000000Z",
  "subject_org": null,
  "subject_location": null,
  "chain": [
    {
      "subject": "northwind.com",
      "issuer": "Let's Encrypt (R3)",
      "is_ca": false,
      "self_signed": false,
      "not_after": "2026-03-30T12:00:00+00:00",
      "fingerprint": "6f6f72db300125c26f8b2c866124ffb57e60a201177144ed5e5b6711eba24cb9"
    },
    {
      "subject": "Let's Encrypt (R3)",
      "issuer": "ISRG Root X1",
      "is_ca": true,
      "self_signed": false,
      "not_after": "2030-01-15T12:00:00+00:00",
      "fingerprint": "e04ca19ed66485fb479283c98fbfb4cda1ab67de1965304902bec3f6a55a2c8e"
    }
  ],
  "chain_status": "trusted",
  "sources": [
    {
      "source": "probe",
      "last_seen": "2026-01-15T06:00:00+00:00",
      "first_seen": "2025-11-16T12:00:00+00:00"
    }
  ],
  "crl_url": null,
  "ocsp_url": null,
  "caa_status": "ok",
  "caissuer_url": "http://r3.i.lencr.org/",
  "no_auto_merge": false,
  "price_yearly": "59.00",
  "price_source": "manual",
  "price_currency": "EUR",
  "billing_interval": "yearly",
  "interval_count": 1,
  "renewal_type": "automatic",
  "renewal_type_basis": "guess",
  "served_status": "served",
  "served_checked_at": "2026-01-15T11:50:00.000000Z",
  "subscription_ends_at": null,
  "seller": null,
  "role": null,
  "ct_finding": null,
  "owner_user_id": null
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.write scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.write",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "validation_failed",
  "message": "The name field is required.",
  "hint": "Fix the fields listed in \"errors\" and send the request again.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#validation_failed",
  "errors": {
    "name": [
      "The name field is required."
    ]
  },
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
422 validation_failed
429 rate_limited


Get a certificate’s timeline ​

The certificate’s history across its renewals: when it was issued, renewed and expired, plus the changes recorded in your audit log. For certificates you renew and pay for yourself, it also marks the day to order the renewal and when the subscription ends.

GET/organizations/{organization}/certificates/{sslCertificate}/timeline
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

sslCertificateintegerrequired

The SSL certificate's ID.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/1/timeline" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/1/timeline" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/1/timeline', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/1/timeline",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/1/timeline', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/1/timeline", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "events": [
    {
      "type": "issued",
      "at": "2025-12-30T12:00:00+00:00",
      "fingerprint": "6f6f72db3001"
    }
  ],
  "generations": 1,
  "markers": []
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


Get certificate coverage per domain ​

For each of your domains, whether a certificate covers it (covered, expiring, expired or uncovered) and the best certificate that does. Superseded and dismissed certificates are left out.

GET/organizations/{organization}/certificates/coverage
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/coverage" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/coverage" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/coverage', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/coverage",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/coverage', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/coverage", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
[
  {
    "domain_id": 11,
    "domain": "northwind.cloud",
    "status": "covered",
    "certificate": {
      "id": 6,
      "subject": "*.northwind.cloud",
      "not_after": "2026-03-17T12:00:00.000000Z",
      "status": "valid",
      "source": "hetzner"
    }
  },
  {
    "domain_id": 1,
    "domain": "northwind.com",
    "status": "covered",
    "certificate": {
      "id": 8,
      "subject": "cdn.northwind.com",
      "not_after": "2026-11-11T12:00:00.000000Z",
      "status": "valid",
      "source": "cloudflare"
    }
  }
]
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


List certificate findings ​

Everything InfraNest found about your certificates that may need attention, from problems to recommendations, with counts per tier.

GET/organizations/{organization}/certificates/findings
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/findings" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/findings" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/findings', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/findings",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/findings', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/findings", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "findings": [
    {
      "tier": "problem",
      "code": "expired",
      "severity": "critical",
      "subject": {
        "type": "certificate",
        "id": 10,
        "name": "vpn.northwind.example"
      },
      "certificate_ids": [
        10
      ],
      "addresses": [],
      "attention_key": "certificate:10",
      "due_at": "2026-01-03T12:00:00+00:00",
      "evidence": {
        "expires_at": "2026-01-03T12:00:00+00:00",
        "days_left": -12,
        "renews_itself": false,
        "renewal_type": "internal",
        "renewal_type_basis": "guess",
        "renewal_source": null,
        "issuer": "vpn.northwind.example",
        "served_status": null,
        "role": null,
        "renewer": null
      },
      "copy": {
        "title": {
          "key": "certificates.rules.expired.title",
          "params": {
            "expires_at": "2026-01-03T12:00:00+00:00",
            "name": "vpn.northwind.example"
          }
        },
        "detail": {
          "key": "certificates.rules.expired.detail",
          "params": {
            "expires_at": "2026-01-03T12:00:00+00:00",
            "name": "vpn.northwind.example"
          }
        },
        "short": {
          "key": "certificates.rules.expired.short",
          "params": {
            "expires_at": "2026-01-03T12:00:00+00:00",
            "name": "vpn.northwind.example"
          }
        }
      },
      "fix": {
        "kind": "route",
        "label": "certificates.rules.expired.fix",
        "route": "/certificates/10"
      },
      "dismissible": false,
      "advisor": null,
      "decision": null,
      "automation": null,
      "sources": [],
      "also": []
    },
    {
      "tier": "problem",
      "code": "domain_expires_first",
      "severity": "warning",
      "subject": {
        "type": "certificate",
        "id": 12,
        "name": "northwind.shop"
      },
      "certificate_ids": [
        12
      ],
      "addresses": [
        "northwind.shop:443"
      ],
      "attention_key": "domain:10",
      "due_at": "2026-01-26T00:00:00+00:00",
      "evidence": {
        "expires_at": "2026-03-17T12:00:00+00:00",
        "days_left": 61,
        "renews_itself": true,
        "renewal_type": "automatic",
        "renewal_type_basis": "guess",
        "renewal_source": "Let's Encrypt (R11)",
        "issuer": "Let's Encrypt (R11)",
        "served_status": "served",
        "role": null,
        "renewer": null,
        "domain_id": 10,
        "domain": "northwind.shop",
        "domain_expires_at": "2026-01-26T00:00:00+00:00",
        "domain_auto_renew": false
      },
      "copy": {
        "title": {
          "key": "certificates.rules.domain_expires_first.title",
          "params": {
            "domain": "northwind.shop",
            "domain_expires_at": "2026-01-26T00:00:00+00:00",
            "expires_at": "2026-03-17T12:00:00+00:00",
            "name": "northwind.shop"
          }
        },
        "detail": {
          "key": "certificates.rules.domain_expires_first.detail",
          "params": {
            "domain": "northwind.shop",
            "domain_expires_at": "2026-01-26T00:00:00+00:00",
            "expires_at": "2026-03-17T12:00:00+00:00",
            "name": "northwind.shop"
          }
        },
        "short": {
          "key": "certificates.rules.domain_expires_first.short",
          "params": {
            "domain": "northwind.shop",
            "domain_expires_at": "2026-01-26T00:00:00+00:00",
            "expires_at": "2026-03-17T12:00:00+00:00",
            "name": "northwind.shop"
          }
        }
      },
      "fix": {
        "kind": "route",
        "label": "certificates.rules.domain_expires_first.fix",
        "route": "/domains/10"
      },
      "dismissible": true,
      "advisor": null,
      "decision": null,
      "automation": null,
      "sources": [],
      "also": []
    }
  ],
  "counts": {
    "problem": 6,
    "recommendation": 6,
    "info": 0
  }
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


Preview domains to add from certificates ​

The domain names your certificates use that InfraNest does not track yet, where each is registered as far as is known, and the certificates that use it. Also says how many more domains your plan allows; nothing is changed.

GET/organizations/{organization}/certificates/fixes/domains
Scope domains.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/fixes/domains" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/fixes/domains" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/fixes/domains', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/fixes/domains",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/fixes/domains', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/fixes/domains", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "domains": [],
  "room": null,
  "allowed": true
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the domains.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "domains.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


List certificate fixes already applied ​

The certificate recommendations already acted on, newest first: monitors and domains added from a recommendation and CAA records added to unblock a renewal, with who did it and when.

GET/organizations/{organization}/certificates/fixes/done
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/fixes/done" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/fixes/done" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/fixes/done', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/fixes/done",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/fixes/done', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/fixes/done", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "lines": []
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


Preview monitors to add for certificates ​

The addresses InfraNest suggests watching with an SSL monitor, the monitor each would get and the certificates involved. Also says how many more monitors your plan allows; nothing is changed.

GET/organizations/{organization}/certificates/fixes/monitors
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/fixes/monitors" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/fixes/monitors" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/fixes/monitors', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/fixes/monitors",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/fixes/monitors', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/fixes/monitors", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "addresses": [
    {
      "address": "archive.northwind.com:443",
      "host": "archive.northwind.com",
      "port": 443,
      "certificates": [
        {
          "id": 18,
          "name": "archive.northwind.com"
        }
      ],
      "monitor": {
        "name": "archive.northwind.com",
        "type": "ssl",
        "target": "archive.northwind.com",
        "interval_seconds": 3600
      },
      "unsupported": null
    },
    {
      "address": "billing.northwind.nl:443",
      "host": "billing.northwind.nl",
      "port": 443,
      "certificates": [
        {
          "id": 14,
          "name": "billing.northwind.nl"
        }
      ],
      "monitor": {
        "name": "billing.northwind.nl",
        "type": "ssl",
        "target": "billing.northwind.nl",
        "interval_seconds": 3600
      },
      "unsupported": null
    }
  ],
  "room": null,
  "allowed": true
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


Get the certificate overview ​

An address-by-address view of the certificates your visitors actually receive, grouped by domain, with a summary, what needs you, the renewals due in the next 90 days and recommendations. Also lists the members who can be made a certificate’s owner.

GET/organizations/{organization}/certificates/overview
Scope monitoring.readRead only
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Request

sh
curl "https://api.infranest.io/api/organizations/42/certificates/overview" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json"
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
Invoke-RestMethod -Method Get -Uri "https://api.infranest.io/api/organizations/42/certificates/overview" -Headers $headers
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.infranest.io/api/organizations/42/certificates/overview', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.get(
    "https://api.infranest.io/api/organizations/42/certificates/overview",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/overview', {
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
  },
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.infranest.io/api/organizations/42/certificates/overview", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "summary": {
    "state": "needs_you",
    "addresses_needing_you": 5,
    "other_problems": 1,
    "first_problem": {
      "endpoint_id": 7
    },
    "todo": [
      {
        "endpoint_id": 7,
        "host": "northwind.shop",
        "port": 443,
        "address": "northwind.shop:443",
        "finding": {
          "tier": "problem",
          "code": "domain_expires_first",
          "severity": "warning",
          "subject": {
            "type": "certificate",
            "id": 12,
            "name": "northwind.shop"
          },
          "certificate_ids": [
            12
          ],
          "addresses": [
            "northwind.shop:443"
          ],
          "attention_key": "domain:10",
          "due_at": "2026-01-26T00:00:00+00:00",
          "evidence": {
            "expires_at": "2026-03-17T12:00:00+00:00",
            "days_left": 61,
            "renews_itself": true,
            "renewal_type": "automatic",
            "renewal_type_basis": "guess",
            "renewal_source": "Let's Encrypt (R11)",
            "issuer": "Let's Encrypt (R11)",
            "served_status": "served",
            "role": null,
            "renewer": null,
            "domain_id": 10,
            "domain": "northwind.shop",
            "domain_expires_at": "2026-01-26T00:00:00+00:00",
            "domain_auto_renew": false
          },
          "copy": {
            "title": {
              "key": "certificates.rules.domain_expires_first.title",
              "params": {
                "domain": "northwind.shop",
                "domain_expires_at": "2026-01-26T00:00:00+00:00",
                "expires_at": "2026-03-17T12:00:00+00:00",
                "name": "northwind.shop"
              }
            },
            "detail": {
              "key": "certificates.rules.domain_expires_first.detail",
              "params": {
                "domain": "northwind.shop",
                "domain_expires_at": "2026-01-26T00:00:00+00:00",
                "expires_at": "2026-03-17T12:00:00+00:00",
                "name": "northwind.shop"
              }
            },
            "short": {
              "key": "certificates.rules.domain_expires_first.short",
              "params": {
                "domain": "northwind.shop",
                "domain_expires_at": "2026-01-26T00:00:00+00:00",
                "expires_at": "2026-03-17T12:00:00+00:00",
                "name": "northwind.shop"
              }
            }
          },
          "fix": {
            "kind": "route",
            "label": "certificates.rules.domain_expires_first.fix",
            "route": "/domains/10"
          },
          "dismissible": true,
          "advisor": null,
          "decision": null,
          "automation": null,
          "sources": [],
          "also": []
        },
        "attention_keys": [
          "domain:10"
        ]
      }
    ],
    "decided": [],
    "addresses": 12,
    "domains": 5,
    "renew_automatically": 8,
    "monitored": 5,
    "recommendations": 6,
    "watched": 1,
    "next_renewal": {
      "host": "staging.northwind-app.dev",
      "more": 0,
      "at": "2026-01-18T00:00:00+00:00",
      "who": "automatic"
    }
  },
  "timeline": [
    {
      "certificate_id": 16,
      "subject": "northwind-mail.com",
      "issuer": "Sectigo RSA Organization Validation Secure Server CA",
      "expires_at": "2026-02-02T12:00:00+00:00",
      "renews_at": null,
      "who": "you",
      "endpoint_ids": [
        11
      ],
      "hosts": [
        "mx1.northwind-mail.com"
      ],
      "listed": true
    }
  ],
  "needs_you": [
    {
      "kind": "problem",
      "at": "2026-01-26T00:00:00+00:00",
      "now": false,
      "severity": "warning",
      "endpoint_id": 7,
      "hosts": [
        "northwind.shop"
      ],
      "reason": {
        "key": "certificates.rules.domain_expires_first.short",
        "params": {
          "domain": "northwind.shop",
          "domain_expires_at": "2026-01-26T00:00:00+00:00",
          "expires_at": "2026-03-17T12:00:00+00:00",
          "name": "northwind.shop"
        }
      },
      "who": null
    }
  ],
  "groups": [
    {
      "key": "domain:northwind-mail.com",
      "domain_id": 16,
      "name": "northwind-mail.com",
      "in_domains": true,
      "rows": [
        {
          "domain_id": 16,
          "endpoint_id": 11,
          "host": "mx1.northwind-mail.com",
          "port": 993,
          "address": "mx1.northwind-mail.com:993",
          "status": "problem",
          "severity": "warning",
          "reason": {
            "key": "certificates.rules.rollout_incomplete.short",
            "params": {
              "address": "mx1.northwind-mail.com:993",
              "name": "northwind-mail.com",
              "done": 1,
              "total": 2,
              "expires_at": "2026-02-02T12:00:00+00:00",
              "server": "mail-01"
            }
          },
          "check_note": null,
          "certificate": {
            "id": 16,
            "subject": "northwind-mail.com",
            "issuer": "Sectigo RSA Organization Validation Secure Server CA",
            "not_after": "2026-02-02T12:00:00+00:00",
            "status": "superseded",
            "role": null
          },
          "origin_certificate": null,
          "served_by": {
            "kind": "server",
            "provider": "transip",
            "provider_label": "TransIP",
            "cloud_server": {
              "id": 14,
              "name": "mail-01"
            },
            "resolved_ips": [
              "198.51.100.166"
            ],
            "edge_network": null,
            "network": null,
            "display": {
              "kind": "server",
              "name": "mail-01",
              "ip": "198.51.100.166"
            },
            "dns_record": {
              "id": 53,
              "type": "A",
              "name": "mx1",
              "value": "198.51.100.166",
              "zone": {
                "id": 9,
                "name": "northwind-mail.com"
              }
            }
          },
          "renewal": {
            "type": "manual_paid",
            "source": null,
            "renews_itself": false,
            "actor": "you"
          },
          "ownership": {
            "is": "yours",
            "basis": "domain",
            "override": null
          },
          "monitor": null,
          "last_checked_at": "2026-01-15T11:50:00+00:00",
          "last_check_source": "scan",
          "last_attempt_at": "2026-01-15T11:50:00+00:00",
          "failing": null,
          "findings": [
            {
              "tier": "problem",
              "code": "rollout_incomplete",
              "severity": "warning",
              "subject": {
                "type": "address",
                "id": 11,
                "name": "mx1.northwind-mail.com:993"
              },
              "certificate_ids": [
                16
              ],
              "addresses": [
                "mx1.northwind-mail.com:993"
              ],
              "attention_key": "certificate_rollout:11",
              "due_at": "2026-02-02T12:00:00+00:00",
              "evidence": {
                "endpoint_id": 11,
                "serves_certificate_id": 16,
                "successor_id": 15,
                "expires_at": "2026-02-02T12:00:00+00:00",
                "done": 1,
                "total": 2,
                "last_checked_at": "2026-01-15T11:50:00+00:00"
              },
              "copy": {
                "title": {
                  "key": "certificates.rules.rollout_incomplete.title",
                  "params": {
                    "address": "mx1.northwind-mail.com:993",
                    "name": "northwind-mail.com",
                    "done": 1,
                    "total": 2,
                    "expires_at": "2026-02-02T12:00:00+00:00"
                  }
                },
                "detail": {
                  "key": "certificates.rules.rollout_incomplete.detailServer",
                  "params": {
                    "address": "mx1.northwind-mail.com:993",
                    "name": "northwind-mail.com",
                    "done": 1,
                    "total": 2,
                    "expires_at": "2026-02-02T12:00:00+00:00",
                    "server": "mail-01"
                  },
                  "count": 2
                },
                "short": {
                  "key": "certificates.rules.rollout_incomplete.short",
                  "params": {
                    "address": "mx1.northwind-mail.com:993",
                    "name": "northwind-mail.com",
                    "done": 1,
                    "total": 2,
                    "expires_at": "2026-02-02T12:00:00+00:00",
                    "server": "mail-01"
                  }
                }
              },
              "fix": {
                "kind": "action",
                "label": "certificates.rules.rollout_incomplete.fix",
                "action": "certificate.scan",
                "method": "POST",
                "endpoint": "/organizations/1/certificates/scan",
                "body": {
                  "host": "mx1.northwind-mail.com",
                  "port": 993
                }
              },
              "dismissible": true,
              "advisor": null,
              "decision": null,
              "automation": {
                "template": "cert_rollout_not_live_alert",
                "action": "send_notification",
                "provider": null,
                "connected": true,
                "conditions": [],
                "title": {
                  "key": "certificates.automate.cert_rollout_not_live_alert",
                  "params": {
                    "days": 0
                  }
                }
              },
              "sources": [
                {
                  "source": "check",
                  "tone": "bad",
                  "copy": {
                    "key": "certificates.evidence.check.oldCertificate",
                    "params": []
                  }
                }
              ],
              "also": []
            }
          ],
          "set_aside": false
        }
      ],
      "count": 2,
      "problems": 1,
      "next_expiry": "2026-02-02T12:00:00+00:00"
    }
  ],
  "watched": [
    {
      "endpoint_id": 13,
      "host": "status.supplier.example",
      "port": 443,
      "address": "status.supplier.example:443",
      "domain_id": null,
      "status": "ok",
      "severity": null,
      "reason": null,
      "check_note": null,
      "certificate": {
        "id": 19,
        "subject": "status.supplier.example",
        "issuer": "Let's Encrypt (R3)",
        "not_after": "2026-03-28T12:00:00+00:00",
        "status": "valid",
        "role": null
      },
      "origin_certificate": null,
      "served_by": {
        "kind": "address",
        "provider": null,
        "provider_label": null,
        "cloud_server": null,
        "resolved_ips": [
          "203.0.113.210"
        ],
        "edge_network": null,
        "network": null,
        "display": {
          "kind": "ip",
          "name": "203.0.113.210",
          "ip": "203.0.113.210"
        },
        "dns_record": null
      },
      "renewal": {
        "type": "automatic",
        "source": "Let's Encrypt (R3)",
        "renews_itself": true,
        "actor": "automatic"
      },
      "ownership": {
        "is": "watched",
        "basis": "none",
        "override": null
      },
      "monitor": {
        "id": 13,
        "name": "Supplier status",
        "status": "up",
        "last_checked_at": "2026-01-15T11:58:00+00:00"
      },
      "last_checked_at": "2026-01-15T11:50:00+00:00",
      "last_check_source": "scan",
      "last_attempt_at": "2026-01-15T11:50:00+00:00",
      "failing": null,
      "findings": []
    }
  ],
  "recommendations": [
    {
      "tier": "recommendation",
      "code": "chain_incomplete",
      "severity": "warning",
      "subject": {
        "type": "certificate",
        "id": 4,
        "name": "docs.northwind.com"
      },
      "certificate_ids": [
        4
      ],
      "addresses": [
        "docs.northwind.com:443"
      ],
      "attention_key": null,
      "due_at": null,
      "evidence": {
        "expires_at": "2026-03-08T12:00:00+00:00",
        "days_left": 52,
        "renews_itself": true,
        "renewal_type": "automatic",
        "renewal_type_basis": "guess",
        "renewal_source": "Let's Encrypt (R11)",
        "issuer": "Let's Encrypt (R11)",
        "served_status": "served",
        "role": null,
        "renewer": null,
        "chain_status": "incomplete"
      },
      "copy": {
        "title": {
          "key": "certificates.rules.chain_incomplete.title",
          "params": {
            "name": "docs.northwind.com"
          }
        },
        "detail": {
          "key": "certificates.rules.chain_incomplete.detail",
          "params": {
            "name": "docs.northwind.com"
          }
        },
        "short": {
          "key": "certificates.rules.chain_incomplete.short",
          "params": {
            "name": "docs.northwind.com"
          }
        }
      },
      "fix": {
        "kind": "route",
        "label": "certificates.rules.chain_incomplete.fix",
        "route": "/certificates/4"
      },
      "dismissible": true,
      "advisor": {
        "resource_type": "certificate",
        "resource_id": "4",
        "check_id": "chain"
      },
      "decision": null,
      "automation": null,
      "sources": [],
      "also": []
    }
  ],
  "recommendation_groups": [
    {
      "code": "chain_incomplete",
      "severity": "warning",
      "items": [
        {
          "tier": "recommendation",
          "code": "chain_incomplete",
          "severity": "warning",
          "subject": {
            "type": "certificate",
            "id": 4,
            "name": "docs.northwind.com"
          },
          "certificate_ids": [
            4
          ],
          "addresses": [
            "docs.northwind.com:443"
          ],
          "attention_key": null,
          "due_at": null,
          "evidence": {
            "expires_at": "2026-03-08T12:00:00+00:00",
            "days_left": 52,
            "renews_itself": true,
            "renewal_type": "automatic",
            "renewal_type_basis": "guess",
            "renewal_source": "Let's Encrypt (R11)",
            "issuer": "Let's Encrypt (R11)",
            "served_status": "served",
            "role": null,
            "renewer": null,
            "chain_status": "incomplete"
          },
          "copy": {
            "title": {
              "key": "certificates.rules.chain_incomplete.title",
              "params": {
                "name": "docs.northwind.com"
              }
            },
            "detail": {
              "key": "certificates.rules.chain_incomplete.detail",
              "params": {
                "name": "docs.northwind.com"
              }
            },
            "short": {
              "key": "certificates.rules.chain_incomplete.short",
              "params": {
                "name": "docs.northwind.com"
              }
            }
          },
          "fix": {
            "kind": "route",
            "label": "certificates.rules.chain_incomplete.fix",
            "route": "/certificates/4"
          },
          "dismissible": true,
          "advisor": {
            "resource_type": "certificate",
            "resource_id": "4",
            "check_id": "chain"
          },
          "decision": null,
          "automation": null,
          "sources": [],
          "also": []
        }
      ],
      "count": 1,
      "copy": {
        "title": {
          "key": "certificates.overview.recGroups.chain_incomplete.title",
          "params": {
            "n": 1
          }
        },
        "detail": {
          "key": "certificates.overview.recGroups.chain_incomplete.detail",
          "params": {
            "n": 1
          }
        }
      },
      "fix": {
        "kind": "route",
        "label": "certificates.rules.chain_incomplete.fix",
        "route": "/certificates/4"
      },
      "dismiss": [
        {
          "resource_type": "certificate",
          "resource_id": "4",
          "check_id": "chain"
        }
      ]
    }
  ],
  "dismissed_recommendations": [],
  "tidied": {
    "replaced": 2,
    "not_served": 0,
    "provider": 0,
    "merged": 1,
    "dismissed": 0,
    "untracked": 0
  },
  "untracked": [],
  "inventory": [
    {
      "id": 10,
      "subject": "vpn.northwind.example",
      "issuer": "vpn.northwind.example",
      "source": "manual",
      "sources": [
        "manual"
      ],
      "not_after": "2026-01-03T12:00:00+00:00",
      "status": "expired",
      "role": "not_seen",
      "hosts": [],
      "superseded_by_id": null
    }
  ],
  "owner_candidates": [
    {
      "id": 1,
      "name": "Alex Rivera"
    }
  ]
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.read scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.read",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied
404 not_found
429 rate_limited


Assign an owner to certificates ​

Makes the member given in user_id the owner of each certificate in certificate_ids (up to 200). Refused when that person is not a member of the organization.

POST/organizations/{organization}/certificates/owner
Scope monitoring.writeChanges data
Authorizationheader
Authorizationstringrequired

Bearer and your API token: Bearer $INFRANEST_TOKEN. See Tokens and scopes.

Path parameters
organizationintegerrequired

Your organization's ID: the number after /organizations/ in the app's address, or from GET /organizations.

Bodyapplication/json
certificate_idsarrayoptional
user_idintegerrequired

Request

sh
curl -X POST "https://api.infranest.io/api/organizations/42/certificates/owner" \
  -H "Authorization: Bearer $INFRANEST_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"certificate_ids":[1],"user_id":1}'
powershell
$headers = @{
  Authorization = "Bearer $env:INFRANEST_TOKEN"
  Accept = "application/json"
}
$body = @'
{
  "certificate_ids": [
    1
  ],
  "user_id": 1
}
'@
Invoke-RestMethod -Method Post -Uri "https://api.infranest.io/api/organizations/42/certificates/owner" -Headers $headers -ContentType "application/json" -Body $body
php
<?php
// composer require guzzlehttp/guzzle
require "vendor/autoload.php";

$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.infranest.io/api/organizations/42/certificates/owner', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('INFRANEST_TOKEN'),
        'Accept' => 'application/json',
    ],
    'json' => [
        'certificate_ids' => [
            1,
        ],
        'user_id' => 1,
    ],
]);

echo $response->getBody();
python
import os

import requests

response = requests.post(
    "https://api.infranest.io/api/organizations/42/certificates/owner",
    headers={
        "Authorization": f"Bearer {os.environ['INFRANEST_TOKEN']}",
        "Accept": "application/json",
    },
    json={
        "certificate_ids": [
            1,
        ],
        "user_id": 1,
    },
)
response.raise_for_status()
print(response.json())
js
const response = await fetch('https://api.infranest.io/api/organizations/42/certificates/owner', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.INFRANEST_TOKEN}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "certificate_ids": [
      1
    ],
    "user_id": 1
  }),
})
console.log(response.status, await response.text())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	req, err := http.NewRequest("POST", "https://api.infranest.io/api/organizations/42/certificates/owner", strings.NewReader(`{"certificate_ids":[1],"user_id":1}`))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRANEST_TOKEN"))
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}

Response

json
{
  "assigned": 1,
  "owner": {
    "id": 1,
    "name": "Alex Rivera"
  }
}
json
{
  "code": "unauthenticated",
  "message": "You are not signed in, or the API token is missing, wrong, expired or revoked.",
  "hint": "Send a valid token in the Authorization header, as the word Bearer followed by the token. Create a new one under My account → Developer if it expired or was revoked.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#unauthenticated",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "scope_missing",
  "message": "This API token does not have the monitoring.write scope.",
  "hint": "Create a token that includes the scope named in \"required\". Give each token only the scopes it needs.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#scope_missing",
  "required": "monitoring.write",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "not_found",
  "message": "Nothing exists at this address, or you cannot see it.",
  "hint": "Check the IDs in the address. A resource in another organization answers as not found.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#not_found",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "idempotency_in_progress",
  "message": "A request with this Idempotency-Key is still being handled.",
  "hint": "Wait a few seconds, then send the same request with the same key again to get its result.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#idempotency_in_progress",
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "validation_failed",
  "message": "The user id field is required.",
  "hint": "Fix the fields listed in \"errors\" and send the request again.",
  "retryable": false,
  "docs_url": "https://developers.infranest.io/errors#validation_failed",
  "errors": {
    "user_id": [
      "The user id field is required."
    ]
  },
  "reference": "k3j9x0b2qa"
}
json
{
  "code": "rate_limited",
  "message": "This token made more than 300 requests in a minute.",
  "hint": "Wait the number of seconds in \"retry_after\" (also the Retry-After header), then retry.",
  "retryable": true,
  "docs_url": "https://developers.infranest.io/errors#rate_limited",
  "limit_key": "api_requests_per_minute",
  "limit": 300,
  "retry_after": 12,
  "reference": "k3j9x0b2qa"
}

401 unauthenticated
403 scope_missing wrong_organization test_token_required test_token_sandbox_only permission_denied write_disabled organization_suspended
404 not_found
409 idempotency_in_progress
422 validation_failed idempotency_key_reused
429 rate_limited