Bulk revoke multiple certificates

Bulk revocation is essential when you need to quickly invalidate large numbers of certificates due to events such as key compromise, misissuance, or other security incidents. For example, when a certificate profile is retired, incorrectly configured, or identified as having issued certificates improperly within a defined timeframe, all certificates issued under that profile during that period should be revoked immediately. DigiCert® Trust Lifecycle Manager allows you to search your certificate inventory using various criteria, then revoke each matching certificate programmatically through the API, providing a repeatable and auditable process for bulk revocation events.

What you’ll learn

In this tutorial, you will:

  • Search your certificate inventory for all active certificates issued under a specified profile within a defined time window (valid_from and valid_to).
  • Revoke each certificate using its serial_number while specifying an appropriate revocation_reason.
  • Confirm that the status of each certificate has been updated to revoked.

Before you begin

A DigiCert ONE account with DigiCert® Trust Lifecycle Manager access.

An API token with the following permissions:

  • Certificates: View certificates and certificate details in inventory.
  • Revoke: Revoke certificates.
The UUID of the certificate profile whose certificates you want to revoke (for example, a1b2c3d4-e5f6-7890-abcd-ef1234567890). You can find this in the Trust Lifecycle Manager UI under Profiles, or from the profile.id field in any certificate detail response.
The start and end dates of the issuance window you want to target, in YYYY-MM-DD_YYYY-MM-DD format (for example, 2026-03-01_2026-03-08 for a one-week period).
The serial number of each certificate is not required in advance. You will retrieve it in Step 1.
jq installed for JSON formatting. Use jq --version to verify.

Endpoint overview

PurposeMethodEndpoint
Search certificatesGET/mpki/api/v1/certificate-search
Revoke certificatePUT/mpki/api/v1/certificate/{serial_number}/revoke
Verify revocation statusGET/mpki/api/v1/certificate/{serial_number}

Revocation reason codes

Every revocation request requires you to specify a reason code that explains why the certificate is being revoked. Choosing the correct reason ensures accurate audit trails and proper compliance with certificate policies. While private certificates support all available reason codes, public and ACME-based certificates are limited to a subset of supported values key_compromise, affiliation_changed, superseded, and cessation_of_operation. Refer to the table below to select the reason that best matches your use case.

Reason codeWhen to useCertificate types
key_compromisePrivate key was exposed or stolenPrivate, Public, ACME
affiliation_changedCertificate holder’s organization changedPrivate, Public, ACME
supersededCertificate was replaced by a new onePrivate, Public, ACME
cessation_of_operationDomain or service is being decommissionedPrivate, Public, ACME
privilege_withdrawnIssuing authority revoked the holder’s entitlementPrivate only
certificate_holdTemporarily suspend the certificate (adds to CRL until removed or expired)Private only
ca_compromiseCertificate Authority was compromisedPrivate only
unspecifiedNo specific reason appliesPrivate only

Step 1: Search certificates by profile and issuance date

To revoke certificates, start by identifying the serial numbers of all active certificates issued under the target profile within the required time window. Use the following certificate search endpoint with the profile_id and valid_from parameters to retrieve the relevant certificates.

Request:

curl -X GET "https://demo.one.digicert.com/mpki/api/v1/certificate-search?profile_id=PROFILE_ID&valid_from=2025-03-01_2025-03-08&status=issued&limit=50&offset=0" \
  -H "x-api-key: SERVICE_API_TOKEN" \
  | jq '.'
import requests

# Configuration
BASE_URL = "https://demo.one.digicert.com"
SERVICE_API_TOKEN = "SERVICE_API_TOKEN"
# Query parameters
PROFILE_ID = "PROFILE_ID"
VALID_FROM = "2025-03-01_2025-03-08"
STATUS = "issued"
LIMIT = 50
OFFSET = 0

response = requests.get(
    f"{BASE_URL}/mpki/api/v1/certificate-search",
    headers={"x-api-key": SERVICE_API_TOKEN},
    params={"profile_id": PROFILE_ID, "valid_from": VALID_FROM, "status": STATUS, "limit": LIMIT, "offset": OFFSET}
)

print(f"Status Code: {response.status_code}")
print(response.json())
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ApiExample {
    public static void main(String[] args) throws Exception {
        // Configuration
        String baseUrl = "https://demo.one.digicert.com";
        String serviceApiToken = "SERVICE_API_TOKEN";
        // Query parameters
        String profileId = "PROFILE_ID";
        String validFrom = "2025-03-01_2025-03-08";
        String status = "issued";
        String limit = "50";
        String offset = "0";

        // Send request
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + "/mpki/api/v1/certificate-search"
                + "?profile_id=" + profileId
                + "&valid_from=" + validFrom
                + "&status=" + status
                + "&limit=" + limit
                + "&offset=" + offset))
            .header("x-api-key", serviceApiToken)
            .GET()
            .build();

        HttpResponse<String> response = client.send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

        System.out.println("Status Code: " + response.statusCode());
        System.out.println(response.body());
    }
}
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // Configuration
        string baseUrl = "https://demo.one.digicert.com";
        string serviceApiToken = "SERVICE_API_TOKEN";
        // Query parameters
        string profileId = "PROFILE_ID";
        string validFrom = "2025-03-01_2025-03-08";
        string status = "issued";
        string limit = "50";
        string offset = "0";

        // Send request
        using var client = new HttpClient();
        client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken);
        var response = await client.GetAsync(
            $"{baseUrl}/mpki/api/v1/certificate-search?profile_id={profileId}&valid_from={validFrom}&status={status}&limit={limit}&offset={offset}");

        string responseBody = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Status Code: {(int)response.StatusCode}");
        Console.WriteLine(responseBody);
    }
}

Specify the profile_id as the UUID of the certificate profile you want to target, and set valid_from to the issuance date range using the YYYY-MM-DD_YYYY-MM-DD format (for example, 2025-03-01_2025-03-08 for a one-week window). To exclude certificates that are already revoked or expired, include status=issued in your request. Use the limit and offset parameters to paginate through the results, noting that the default page size is 20 certificates. If no value is provided for the available query parameters, the system will consider default values.

Successful response (200 OK):


 {
  "total": 28,
  "offset": 0,
  "limit": 50,
  "items": [
    {
      "profile": {
        "id": "7f606454-e011-4cfb-af90-5a13232bbd8f",
        "name": "Example Certificate Profile"
      },
      "seat": {
        "seat_id": "some-seat-id@example.com"
      },
      "business_unit": {
        "id": "12d3c45e-d3e6-46fc-7f48-91030fa5cac6",
        "name": "Example-BU1"
      },
      "account": {
        "id": "f3f99122-1234-567a-919b-5f692216ff85"
      },
      "certificate": "...(Base64-encoded certificate)...",
      "ica": {
        "id": "444FFF3A03C12BCEF463B5D2EF01985F"
      },
      "common_name": "host1.example.com",
      "status": "issued",
      "serial_number": "0F6F55555658A989F0F463A4DA0D807F",
      "thumbprint": "FE0EFDC64AA55565556152850D2B84B46EF433444A28970F6A2A7F510A7A8880",
      "valid_from": "2025-03-01T00:00:00Z",
      "valid_to": "2026-03-08T23:59:59Z",
      "notes": "Some example notes about the valid certificate",
      "subject": {
        "common_name": "host1.example.com",
        "organization_name": "Example Org",
        "organization_units": "Example Org Unit",
        "description": "Some example description",
        "serial_number": "0F6F55555658A989F0F463A4DA0D807F",
        "pseudonym": "Some example pseudonym",
        "unique_identifier": "3a3aeb976040eed5c5d3bfb6143b7814",
        "user_identifier": "Some example user identifier",
        "domain_component": "Some example domain component",
        "dn_qualifier": "Some example DN qualifier",
        "unstructured_name": "Some example unstructured name",
        "unstructured_address": "Some example unstructured address",
        "street_address": "Some example street address",
        "postal_code": 89714,
        "locality": "Carson city",
        "state": "NV",
        "country": "US",
        "email": "some@mail",
        "title": "Some example title",
        "given_name": "Some example given name",
        "surname": "Some example surname",
        "organization_identifier": "1.3.6.1.4.1.311.60.2.1.3"
      }
    }
  ]
}

The response returns a paginated result with a total count and an items array. Each item represents a certificate enrolled under the target profile within the specified window. The profile.id in each item will match the profile_id you provided. Each item also includes a subject object with the full distinguished name fields, and additional metadata fields such as account, notes, and attributes depending on how the certificate was enrolled.

Check the total field. If total is greater than your limit, calculate how many additional pages you need and repeat the request with increasing offset values (offset=20, offset=40, and so on) until you have collected all matching serial numbers. Collect the serial_number and thumbprint values from every item and pass the serial number to the revocation endpoint in Step 2. The thumbprint is required if a serial number collision occurs.

Step 2: Revoke each certificate

With the list of serial numbers from Step 1, revoke each certificate individually by calling the revocation endpoint for each one. The API does not support batch revocation in a single request, hence you must submit one revocation call for each certificate you want to revoke.

Request:

curl -X PUT "https://demo.one.digicert.com/mpki/api/v1/certificate/SERIAL_NUMBER/revoke" \
  -H "x-api-key: SERVICE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"revocation_reason": "privilege_withdrawn"}' \
  | jq '.'
import requests

# Configuration
BASE_URL = "https://demo.one.digicert.com"
SERVICE_API_TOKEN = "SERVICE_API_TOKEN"
# Path parameter
SERIAL_NUMBER = "SERIAL_NUMBER"

response = requests.put(
    f"{BASE_URL}/mpki/api/v1/certificate/{SERIAL_NUMBER}/revoke",
    headers={
        "x-api-key": SERVICE_API_TOKEN,
        "Content-Type": "application/json"
    },
    json={"revocation_reason": "privilege_withdrawn"}
)

print(f"Status Code: {response.status_code}")
print(response.json())
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class ApiExample {
    public static void main(String[] args) throws Exception {
        // Configuration
        String baseUrl = "https://demo.one.digicert.com";
        String serviceApiToken = "SERVICE_API_TOKEN";
        // Path parameter
        String serialNumber = "SERIAL_NUMBER";

        // Build payload
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode payload = mapper.createObjectNode();
        payload.put("revocation_reason", "privilege_withdrawn");

        // Send request
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + "/mpki/api/v1/certificate/" + serialNumber + "/revoke"))
            .header("x-api-key", serviceApiToken)
            .header("Content-Type", "application/json")
            .PUT(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload)))
            .build();

        HttpResponse<String> response = client.send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

        System.out.println("Status Code: " + response.statusCode());
        System.out.println(response.body());
    }
}
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // Configuration
        string baseUrl = "https://demo.one.digicert.com";
        string serviceApiToken = "SERVICE_API_TOKEN";
        // Path parameter
        string serialNumber = "SERIAL_NUMBER";

        // Build payload
        var payload = new JsonObject
        {
            ["revocation_reason"] = "privilege_withdrawn"
        };

        // Send request
        using var client = new HttpClient();
        client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken);

        var content = new StringContent(
            payload.ToJsonString(),
            Encoding.UTF8,
            "application/json"
        );

        var response = await client.PutAsync(
            $"{baseUrl}/mpki/api/v1/certificate/{serialNumber}/revoke", content);

        string responseBody = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Status Code: {(int)response.StatusCode}");
        Console.WriteLine(responseBody);
    }
}

Replace {serial_number} in the endpoint path with the serial number of the certificate you are revoking. Set revocation_reason in the request body with the appropriate reason code from the reference table above. Use the same reason code for all certificates in the same incident. This makes your audit trail consistent.

Successful response (204 No Content):

A 204 No Content response confirms the revocation was accepted. There is no response body. Record the serial number as successfully revoked and proceed to the next certificate in your list. Repeat this step for each serial number collected in Step 1.

Step 3: Verify revocation status

After revoking each certificate, confirm that its status has updated to revoked in Trust Lifecycle Manager. This is especially important for audit trails and compliance reporting. To do this, call the certificate details endpoint for each revoked certificate using its serial number. If the revocation has propagated successfully, the status field in the response will show revoked, and the revocation object will contain the reason code you specified along with a revocation_date timestamp. If the status still shows issued, the revocation may not have propagated yet. Wait a few seconds and retry the details request until you see the updated status. Repeat this verification for each certificate in your list to confirm the bulk operation is complete.

Request:

curl -X GET "https://demo.one.digicert.com/mpki/api/v1/certificate/SERIAL_NUMBER" \
  -H "x-api-key: SERVICE_API_TOKEN" \
  | jq '.'
import requests

# Configuration
BASE_URL = "https://demo.one.digicert.com"
SERVICE_API_TOKEN = "SERVICE_API_TOKEN"
# Path parameter
SERIAL_NUMBER = "SERIAL_NUMBER"

response = requests.get(
    f"{BASE_URL}/mpki/api/v1/certificate/{SERIAL_NUMBER}",
    headers={"x-api-key": SERVICE_API_TOKEN}
)

print(f"Status Code: {response.status_code}")
print(response.json())
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ApiExample {
    public static void main(String[] args) throws Exception {
        // Configuration
        String baseUrl = "https://demo.one.digicert.com";
        String serviceApiToken = "SERVICE_API_TOKEN";
        // Path parameter
        String serialNumber = "SERIAL_NUMBER";

        // Send request
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + "/mpki/api/v1/certificate/" + serialNumber))
            .header("x-api-key", serviceApiToken)
            .GET()
            .build();

        HttpResponse<String> response = client.send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

        System.out.println("Status Code: " + response.statusCode());
        System.out.println(response.body());
    }
}
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // Configuration
        string baseUrl = "https://demo.one.digicert.com";
        string serviceApiToken = "SERVICE_API_TOKEN";
        // Path parameter
        string serialNumber = "SERIAL_NUMBER";

        // Send request
        using var client = new HttpClient();
        client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken);
        var response = await client.GetAsync($"{baseUrl}/mpki/api/v1/certificate/{serialNumber}");

        string responseBody = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"Status Code: {(int)response.StatusCode}");
        Console.WriteLine(responseBody);
    }
}

Replace {serial_number} in the endpoint path with the serial number of the certificate you revoked in Step 2. If the serial number is not unique, include the SHA-256 thumbprint as a query parameter using ?thumbprint_sha256={thumbprint} to ensure the correct certificate is referenced. To know more, see Serial number collision in the Common errors and solutions section.

Successful response (200 OK):

{
  "profile": {
    "id": "7f606454-e011-4cfb-af90-5a13232bbd8f",
    "name": "Example Certificate Profile"
  },
  "seat": {
    "seat_id": "some-seat-id@example.com"
  },
  "business_unit": {
    "id": "12d3c45e-d3e6-46fc-7f48-91030fa5cac6",
    "name": "Example-BU1"
  },
  "account": {
    "id": "f3f99122-1234-567a-919b-5f692216ff85"
  },
  "certificate": "...(Base64-encoded certificate)...",
  "ica": {
    "id": "444FFF3A03C12BCEF463B5D2EF01985F"
  },
  "common_name": "host1.example.com",
  "status": "revoked",
  "serial_number": "0F6F55555658A989F0F463A4DA0D807F",
  "thumbprint": "FE0EFDC64AA55565556152850D2B84B46EF433444A28970F6A2A7F510A7A8880",
  "valid_from": "2025-03-01T00:00:00Z",
  "valid_to": "2026-03-08T23:59:59Z",
  "notes": "Some example notes about the valid certificate",
  "revocation": {
    "reason": "privilege_withdrawn",
    "revocation_date": "2026-03-23T00:00:00Z",
    "comments": ""
    },  
  "template_name": "Template Name",
  "issuing_ca_name": "Issuing CA Name",
  "key_size": 2048,
  "signature_algorithm": "RSA",
  "subject": {
    "common_name": "host1.example.com",
    "organization_name": "Example Org",
    "organization_units": "Example Org Unit",
    "description": "Some example description",
    "serial_number": "0F6F55555658A989F0F463A4DA0D807F",
    "pseudonym": "Some example pseudonym",
    "unique_identifier": "3a3aeb976040eed5c5d3bfb6143b7814",
    "user_identifier": "Some example user identifier",
    "domain_component": "Some example domain component",
    "dn_qualifier": "Some example DN qualifier",
    "unstructured_name": "Some example unstructured name",
    "unstructured_address": "Some example unstructured address",
    "street_address": "Some example street address",
    "postal_code": 89714,
    "locality": "Carson city",
    "state": "NV",
    "country": "US",
    "email": "some@mail",
    "title": "Some example title",
    "given_name": "Some example given name",
    "surname": "Some example surname",
    "organization_identifier": "1.3.6.1.4.1.311.60.2.1.3"
  },
  "cert_security_rating": "Secure",
  "security_rating_info": {
    "security_rating": {
      "encryption": [
        {
          "category_name": "Encryption",
          "criteria_name": "AES256",
          "value": "Strong",
          "rating": {
            "value": true,
            "issue": "None"
          },
          "data": {
            "key_size": 256,
            "mode": "CBC"
          }
        }
      ]
    },
    "handshake_protocols": [
      {
        "cipher_algorithm": "ECDHE-RSA-AES256-GCM-SHA384",
        "key_size": 256,
        "protocol": "TLSv1.2"
      }
    ],
    "security_headers": {
      "X-Content-Type-Options": "nosniff"
    },
    "http_response_headers": {
      "Content-Security-Policy": "default-src 'self'"
    },
    "certificate_chain": {
      "cert_chain_details": [
        {
          "cert_chain_id": 1,
          "overall_status": "Valid",
          "is_additional_cert_chain": false,
          "is_valid_chain": true,
          "cert_details": [
            {
              "status": "Active",
              "expiration_date": 1672531199000,
              "key_algorithm": "RSA",
              "algorithm_type": "SHA256",
              "issue_date": 1609459200000,
              "issuer_DN": "CN=Example CA, O=Example Inc., C=US",
              "subject": "CN=example.com, O=Example Inc., C=US",
              "errors": []
            }
          ]
        }
      ]
    },
    "security_rating_score": "Secure",
    "certificate_chain_score": "Secure",
    "handshake_protocol_score": [
      "Secure"
    ],
    "security_headers_score": "Secure",
    "http_response_headers_score": 10,
    "protocol_ciphers": [
      {
        "protocol": "TLSv1.2",
        "insecure_ciphers": [
          {
            "code": 1,
            "forward_secrecy": false,
            "kex_info": "RSA",
            "name": "RC4",
            "unsafe": 1
          }
        ],
        "weak_ciphers": [],
        "safe_ciphers": [
          {
            "code": 156,
            "forward_secrecy": true,
            "kex_info": "ECDHE-RSA",
            "name": "AES256-GCM-SHA384",
            "unsafe": 0
          }
        ],
        "compressors": "DEFLATE",
        "cipher_pref_order": "client",
        "warnings": ""
      }
    ],
    "protocol_cipher_score": {
      "protocols": [
        "TLSv1.2",
        "TLSv1.3"
      ],
      "score": "Secure"
    }
  }
}

The response returns the full certificate record. Confirm that the status field is revoked. The revocation object nested in the response will contain the reason code you specified and the revocation_date timestamp. If the status field still shows issued, the revocation may not have happened yet. In such case, wait for a few seconds and retry. Repeat this verification for each certificate in your list to confirm the bulk operation is complete.

Common errors and solutions

For general API errors (authentication, rate limits), see Trust Lifecycle Manager API error reference.

Serial number collision

{
  "errors": [
    {
      "code": "collision_error",
      "message": "Collision occurred! Please specify additional parameter: thumbprint_sha256"
    }
  ]
}

Two or more certificates in your account share the same serial number. Retry the revocation request with the SHA-256 thumbprint appended as a query parameter: PUT /mpki/api/v1/certificate/{serial_number}/revoke?thumbprint_sha256={thumbprint}. The thumbprint value was returned in the thumbprint field of the certificate search response in Step 1.

Certificate already revoked

If you attempt to revoke a certificate that is already in revoked status, the API may return an error indicating the certificate is not in a revocable state. Remove revoked certificates from your working list by filtering for status=issued in Step 1 before starting the revocation loop.

No certificates returned for CA and date range

If Step 1 returns an empty items array with "total": 0, it means no issued certificates match the search criteria you specified. In such case, verify that the issuing_ca_id is correct and the date range is properly formatted as YYYY-MM-DD_YYYY-MM-DD. Also note that the valid_from filter applies to the certificate’s validity start date. The backdated certificates issued within the selected time window may fall outside the specified range and therefore not appear in the results.

What’s next?

Now that you have bulk revoked certificates enrolled under the target profile, you can:

  • Monitor certificate inventory: Run periodic searches using profile_id={profile_id}&valid_from=2026-03-01_2026-03-08&status=issued to confirm no active certificates remain under the affected profile. See Search certificates in the API reference.
  • Issue replacement certificates: If this revocation was part of a profile update rather than a security incident, use the certificate issuance endpoint (POST /mpki/api/v1/certificate) to enroll new certificates under the updated profile.
  • Automate certificate lifecycle management: Configure automation profiles in Trust Lifecycle Manager to renew or replace certificates before expiry, reducing the risk of future bulk revocations. See Automate certificate lifecycle management for setup instructions.
  • Export a revocation audit report: Use the reporting features in Trust Lifecycle Manager to generate an audit trail of the revocation event, including timestamps and reason codes, for compliance and incident documentation purposes.