14. API
Read-only REST access to collections, certificates, monitored hosts, and agents.
The CertKit API exposes the state of your account over HTTPS as JSON: which certificates exist, whether they are healthy, what each monitored host is serving, and what every agent is doing. It is designed for dashboards, CMDB syncs, compliance reports, and CI checks that need to ask “is everything green?” without a login.
The current release is v1 and is read-only. Every endpoint is a GET.
Base URL and versioning
https://app.certkit.io/api/public/v1
The version is part of the path. New fields may appear in v1 responses as the API grows.
Authentication
Every request carries an API key as a bearer token:
Authorization: Bearer ck_...
API keys are account-scoped. A key can read every Collection in the account that created it, regardless of the user who created it or that user’s Collection assignments. There are no per-key permissions or per-Collection scopes yet.
Creating a key
- Go to Settings › API Settings (
/settings/api-keys). Account Administrator access is required. - Click Create API Key and give it a name that identifies the consumer (
CI pipeline,Grafana,Terraform). - Copy the key from the confirmation dialog. It is shown exactly once. CertKit stores only a SHA-256 hash and the first 11 characters for display.
Keys start with ck_ and are 46 characters long. Deleting a key revokes it immediately; requests in flight with that key receive 401. The API Settings page shows when each key was last used, and key creation and deletion are recorded in the Activity Log.
Response format
- JSON with
camelCaseproperty names. - Timestamps are ISO 8601 with a UTC offset, for example
2026-09-04T09:15:02+00:00. Nullable timestamps arenull, never omitted. - IDs are short opaque strings (
bfwe,pw08). They match the IDs in dashboard URLs and are stable for the life of the object. - List responses carry a
countand an array. There is no pagination; each call returns the complete set. - Status fields use a three-value scale, the same one the dashboard renders as colored circles:
status |
Meaning |
|---|---|
Good |
Healthy. Nothing to do. |
Ok |
Working, but needs attention soon: expiring, pending sync, waiting on a deploy window. |
Bad |
Failed or expired. Something is broken. |
Unknown |
No data yet. Only on certificates and monitored hosts that have never been checked. |
statusText is the human-readable reason beside each status. It is meant for display and log lines. Branch on status, not statusText, since the wording can change between releases.
Errors
Errors return an appropriate HTTP status and a JSON body with a single error string:
{ "error": "Collection not found." }
| Status | When |
|---|---|
401 Unauthorized |
Missing or malformed Authorization header, or a key that does not exist (including deleted keys). The response includes WWW-Authenticate: Bearer. |
403 Forbidden |
The key is valid but the account’s plan does not include API access, or the subscription or trial has expired. |
404 Not Found |
The Collection ID does not exist or belongs to a different account. The API does not distinguish between the two. |
429 Too Many Requests |
Rate limit exceeded. See below. |
Rate limits
Limits are enforced per API key with a token bucket:
| Limit | |
|---|---|
| Burst | 20 requests |
| Sustained | 30 requests per minute (one token every 2 seconds) |
A caller that has been idle can fire 20 requests back to back, then continue at 30 per minute indefinitely. Requests beyond the limit are rejected immediately with 429; nothing is queued. The response carries a Retry-After header with the number of seconds to wait, and the body repeats it:
{ "error": "Too many requests. Retry after 2 seconds." }
For a polling integration, one call per Collection per endpoint every few minutes is comfortably within the limit. If you need higher throughput, contact us.
Endpoints
List collections
GET /api/public/v1/collections
Returns every Certificate Collection in the account, oldest first.
curl https://app.certkit.io/api/public/v1/collections \
-H "Authorization: Bearer ck_your_api_key"
$headers = @{ Authorization = "Bearer ck_your_api_key" }
(Invoke-RestMethod https://app.certkit.io/api/public/v1/collections -Headers $headers).collections
{
"count": 1,
"collections": [
{
"id": "bfwe",
"name": "Production",
"created": "2026-09-03T18:04:47+00:00"
}
]
}
| Field | Type | Description |
|---|---|---|
id |
string | Collection ID. Use it in the other endpoints. |
name |
string | Display name. |
created |
timestamp, nullable | When the Collection was created. |
List certificates
GET /api/public/v1/collections/{collectionId}/certificates
Returns every certificate configured in the Collection, oldest first, with its current lifecycle status and the most recently issued certificate.
curl https://app.certkit.io/api/public/v1/collections/bfwe/certificates \
-H "Authorization: Bearer ck_your_api_key"
{
"count": 1,
"certificates": [
{
"id": "pw08",
"commonName": "www.example.com",
"description": "Marketing site",
"keyAlgorithm": "EC256",
"sans": ["www.example.com", "example.com"],
"issuer": "Let's Encrypt",
"status": "Good",
"statusText": "Certificate is valid and is up to date.",
"latestCertificate": {
"thumbprint": "7A3F0C4D9E21B85F6A0D3C7E5B19F2A4C8D6E0B1",
"sha256": "2B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFE",
"issued": "2026-08-01T14:02:11+00:00",
"expires": "2026-10-30T14:02:10+00:00",
"renews": "2026-10-01T02:14:38+00:00"
}
}
]
}
| Field | Type | Description |
|---|---|---|
id |
string | Certificate ID. Deployments reference it via certificateId. |
commonName |
string | Subject Common Name. |
description |
string | Free-text description from the certificate settings. |
keyAlgorithm |
string | EC256, EC384, RSA2048, or RSA4096. See Signatures & Key Algorithms. |
sans |
string[] | Every name the certificate covers, Common Name included. |
issuer |
string | Display name of the ACME issuer or Private CA. |
status |
string | Good, Ok, Bad, or Unknown. |
statusText |
string | Reason for the status: expiring soon, waiting for ARI renewal, issuance failed, and so on. |
latestCertificate |
object, nullable | The newest issued certificate. null if nothing has been issued yet. |
latestCertificate.thumbprint |
string | SHA-1 fingerprint, uppercase hex. |
latestCertificate.sha256 |
string | SHA-256 fingerprint, uppercase hex. |
latestCertificate.issued |
timestamp | NotBefore of the issued certificate. |
latestCertificate.expires |
timestamp | NotAfter of the issued certificate. |
latestCertificate.renews |
timestamp | When CertKit plans to renew. Reflects the ARI window when the CA supports it. |
Compare latestCertificate.thumbprint with the thumbprint reported by monitoring or an agent to confirm the newest certificate is actually deployed.
List monitored hosts
GET /api/public/v1/collections/{collectionId}/monitoring
Returns every monitored host in the Collection, sorted by hostname then port, with the result of the most recent check.
curl https://app.certkit.io/api/public/v1/collections/bfwe/monitoring \
-H "Authorization: Bearer ck_your_api_key"
{
"count": 2,
"hosts": [
{
"id": "kq3v",
"host": "www.example.com",
"port": 443,
"description": "Marketing site",
"hostVisibility": "public",
"status": "Good",
"statusText": "Using latest CertKit issued certificate.",
"lastMonitored": "2026-09-04T09:15:02+00:00",
"latestCertificate": {
"thumbprint": "7A3F0C4D9E21B85F6A0D3C7E5B19F2A4C8D6E0B1",
"sha256": "2B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFE",
"expires": "2026-10-30T14:02:10+00:00"
}
},
{
"id": "kq3w",
"host": "portal.corp.example.com",
"port": 443,
"description": "Internal portal",
"hostVisibility": "internal",
"agent": { "name": "WEB-01", "id": "m4xr" },
"status": "Ok",
"statusText": "Certificate expires in 12 days.",
"lastMonitored": "2026-09-04T09:14:37+00:00",
"latestCertificate": {
"thumbprint": "9C2A4F6B8D0E1A3C5F7B9D2E4A6C8F0B1D3E5A7C",
"sha256": "6A09E667BB67AE853C6EF372A54FF53A510E527F9B05688C1F83D9AB5BE0CD19",
"expires": "2026-09-16T00:00:00+00:00"
}
}
]
}
| Field | Type | Description |
|---|---|---|
id |
string | Monitored host ID. |
host |
string | Hostname or IP address checked. |
port |
integer | TCP port checked. |
description |
string | Free-text description. |
hostVisibility |
string | public for hosts checked from CertKit’s monitoring IPs; internal for hosts checked by an agent. See Intranet Host Monitoring. |
agent |
object | Present only on internal hosts. The name and id of the agent performing the check. |
status |
string | Good, Ok, Bad, or Unknown. |
statusText |
string | Reason for the status, including chain validation failures. |
lastMonitored |
timestamp, nullable | When the host was last checked. null if never. |
latestCertificate |
object, nullable | The certificate observed at the last successful handshake. null if no certificate has been seen. |
latestCertificate.thumbprint |
string | SHA-1 fingerprint, uppercase hex. |
latestCertificate.sha256 |
string, nullable | SHA-256 fingerprint, uppercase hex. |
latestCertificate.expires |
timestamp, nullable | NotAfter of the observed certificate. |
List agents
GET /api/public/v1/collections/{collectionId}/agents
Returns every agent in the Collection, sorted by name, with its overall health, the deployment configs it runs, and the internal hosts it monitors.
curl https://app.certkit.io/api/public/v1/collections/bfwe/agents \
-H "Authorization: Bearer ck_your_api_key"
{
"count": 1,
"agents": [
{
"id": "m4xr",
"name": "WEB-01",
"hostname": "WEB-01",
"operatingSystem": "windows",
"version": "1.13.2",
"status": "Bad",
"statusText": "1 of 2 configs failed",
"lastSeen": "2026-09-04T09:14:37+00:00",
"deployments": [
{
"id": "d7kq",
"name": "IIS site binding",
"type": "windows-iis-binding",
"format": "Windows Certificate Store",
"certificateId": "pw08",
"paused": false,
"status": "Good",
"statusText": "Synced",
"message": null
},
{
"id": "d7kr",
"name": "nginx",
"type": null,
"format": "PEM (Cert + Key + Chain)",
"certificateId": "pw08",
"paused": false,
"status": "Bad",
"statusText": "Update Command Failed",
"message": "nginx: [emerg] unknown directive \"ssl_certificat\""
}
],
"monitors": [
{
"id": "kq3w",
"host": "portal.corp.example.com",
"port": 443,
"status": "Ok",
"statusText": "Certificate expires in 12 days.",
"lastMonitored": "2026-09-04T09:14:37+00:00"
}
]
}
]
}
| Field | Type | Description |
|---|---|---|
id |
string | Agent ID. |
name |
string | Display name set in the dashboard. Defaults to the hostname. |
hostname |
string | Machine name reported by the agent. |
operatingSystem |
string | windows or linux. |
version |
string | Agent version at last check-in. |
status |
string | Good, Ok, or Bad. Rolls up the agent’s connectivity and all of its deployments. |
statusText |
string | Summary such as 2 configs synced or Offline. |
lastSeen |
timestamp, nullable | Last check-in. An agent that has stopped checking in reports Offline. |
deployments |
array | One entry per deployment config assigned to this agent. |
deployments[].type |
string, nullable | The built-in template ID, for example windows-iis-binding or fortigate. null for hand-written configs and custom templates. |
deployments[].format |
string | Output format: Windows Certificate Store, PKCS#12 (.pfx), Java KeyStore (.jks), PEM (Cert + Key), PEM (Cert + Key + Chain), or PEM (All-in-one). |
deployments[].certificateId |
string | The certificate this config deploys. Joins to the certificates endpoint. |
deployments[].paused |
boolean | true when an administrator has paused the config. |
deployments[].status |
string | Good, Ok, or Bad. |
deployments[].statusText |
string | Synced, Pending Sync, Waiting for Deploy Window, Update Command Failed, Write Certificates Failed, Not Approved, Not Configured, Offline, or Configuration paused. |
deployments[].message |
string, nullable | Output captured from the last failed update command, when there is one. The fastest way to see why a deployment is red without opening the dashboard. |
monitors |
array | Internal hosts this agent checks. Same shape as the monitoring endpoint minus description, hostVisibility, and latestCertificate. |
Example: fail a pipeline on anything red
Walk every Collection and exit non-zero if any certificate, host, or agent is Bad:
#!/usr/bin/env bash
set -euo pipefail
API=https://app.certkit.io/api/public/v1
AUTH="Authorization: Bearer $CERTKIT_API_KEY"
bad=0
for id in $(curl -sf "$API/collections" -H "$AUTH" | jq -r '.collections[].id'); do
for endpoint in certificates monitoring agents; do
bad=$((bad + $(curl -sf "$API/collections/$id/$endpoint" -H "$AUTH" \
| jq '[.. | objects | select(.status? == "Bad")] | length')))
done
done
echo "Bad items: $bad"
[ "$bad" -eq 0 ]
$api = "https://app.certkit.io/api/public/v1"
$headers = @{ Authorization = "Bearer $env:CERTKIT_API_KEY" }
$bad = @()
foreach ($c in (Invoke-RestMethod "$api/collections" -Headers $headers).collections) {
$bad += (Invoke-RestMethod "$api/collections/$($c.id)/certificates" -Headers $headers).certificates | Where-Object status -eq "Bad"
$bad += (Invoke-RestMethod "$api/collections/$($c.id)/monitoring" -Headers $headers).hosts | Where-Object status -eq "Bad"
$bad += (Invoke-RestMethod "$api/collections/$($c.id)/agents" -Headers $headers).agents | Where-Object status -eq "Bad"
}
$bad | Format-Table id, status, statusText
if ($bad.Count -gt 0) { exit 1 }
The jq filter counts every object at any depth with "status": "Bad", so a single call per endpoint covers nested deployments and monitors too.
More endpoints
v1 is intentionally small, and we are adding to it. If an endpoint would unblock an integration you are building, let us know and tell us what you would do with it.