--- title: "Register a single device" description: "Create a division, attach a bootstrap certificate policy to a device group, and register a single device.\n" source_url: https://dev.digicert.com/device-trust-api/tutorials/register-a-single-device.html --- Device registration is the gate every IoT device passes through before it can request certificates, receive policy, or report state to DigiCert® Device Trust Manager. This tutorial explains how to use API calls to wire device onboarding into your provisioning pipeline. In this tutorial, you will: - Create a division to scope your devices and policies. - Retrieve the division's ID for use in later steps. - Create a bootstrap certificate policy that defines how the device's first certificate is issued. - Create a device group and attach the bootstrap policy. - Retrieve the device group's ID for use in registration. - Register the device against the device group. ## Before you begin Before you begin, make sure you have: - A DigiCert® ONE account with Device Trust Manager access. - An API key with the following role: - `DEVCTM_SOLUTION_ADMINISTRATOR` Provides division, policy, device group, and device creation. - An issuing CA (ICA) configured in DigiCert® Private CA. You will need its `ica_id` for [Step 3](https://dev.digicert.com/md/device-trust-api/tutorials/register-a-single-device.md#step-3-create-a-bootstrap-certificate-policy). - A certificate profile configured in DigiCert Private CA. You will need its `certificate_profile_id` for [Step 3](https://dev.digicert.com/md/device-trust-api/tutorials/register-a-single-device.md#step-3-create-a-bootstrap-certificate-policy). - A rendezvous zone configured in Device Trust Manager. You will need its `primary_rzone_id` for [Step 1](https://dev.digicert.com/md/device-trust-api/tutorials/register-a-single-device.md#step-1-create-a-division). You can retrieve it via the [List all rendezvous zones endpoint](https://dev.digicert.com/md/device-trust-api/api-reference.md#/operations/listRendezvousZones). - Your DigiCert ONE `account_id`. You can retrieve it via the DigiCert® Account Manager [List accounts endpoint](https://dev.digicert.com/md/account-manager-api/api-reference.md#/operations/listAccounts). - A certificate signing request (CSR) or bare public key for the device you are registering. You present this to the bootstrap policy in Step 6. ## Endpoint overview | Method | Path | Description | |--------|------|-------------| | POST | `/devicetrustmanager/api/v4/division` | Create a division | | GET | `/devicetrustmanager/api/v4/division` | List divisions to retrieve the division ID | | POST | `/devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy` | Create a bootstrap certificate policy | | POST | `/devicetrustmanager/api/v4/device-group` | Create a device group | | GET | `/devicetrustmanager/api/v4/device-group` | List device groups to retrieve the device group ID | | POST | `/devicetrustmanager/api/v4/device/registration` | Register a device | ## Reference values These enum-typed fields appear across the request bodies in Steps 3 and 4. Use this table as a quick lookup when constructing requests programmatically. | Field | Used in | Allowed values | |-------|---------|----------------| | `certificate_management_methods` | [Step 3](#step-3-create-a-bootstrap-certificate-policy) | `SINGLE`, `BATCH`, `TRUST_EDGE`, `EST`, `SCEP`, `CMP_V2`, `ACME` | | `key_generation_option` | [Step 3](#step-3-create-a-bootstrap-certificate-policy) | `client_side`, `server_side`, `client_or_server_side` | | `key_generation_type` | [Step 3](#step-3-create-a-bootstrap-certificate-policy) | `RSA_2048`, `RSA_3072`, `RSA_4096`, `P_256`, `P_384`, `P_512`, `ED_25519` | | `policies[].type` | [Step 4](#step-4-create-a-device-group-with-the-bootstrap-policy-attached) | `bootstrapCertificate`, `operationalCertificate` | | `policies[].status` | [Step 4](#step-4-create-a-device-group-with-the-bootstrap-policy-attached) | `ACTIVE`, `DISABLED`, `DELETED`, `INVITE_SENT`, `READY`, `EXPIRED` | ## Step 1: Create a division A division is an isolated container for your devices, policies, and device groups. Most production deployments organize divisions by business unit, product line, or customer tenant. Create one before any device-related resources so every resource you create afterward belongs to a known scope. **Request:** **curl** ```bash curl -X POST "https://demo.one.digicert.com/devicetrustmanager/api/v4/division" \ -H "x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Example division name", "description": "Example division description.", "account_id": "ACCOUNT_ID", "primary_rzone_id": "PRIMARY_RZONE_ID" }' | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY = "DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" # Request details NAME = "Example division name" DESCRIPTION = "Example division description." ACCOUNT_ID = "ACCOUNT_ID" PRIMARY_RZONE_ID = "PRIMARY_RZONE_ID" payload = { "name": NAME, "description": DESCRIPTION, "account_id": ACCOUNT_ID, "primary_rzone_id": PRIMARY_RZONE_ID, } response = requests.post( f"{BASE_URL}/devicetrustmanager/api/v4/division", headers={ "x-api-key": DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY, "Content-Type": "application/json" }, json=payload ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java 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 adminApiKey = "ADMIN_API_KEY"; // Request details String name = "Example division name"; String description = "Example division description."; String accountId = "ACCOUNT_ID"; String primaryRzoneId = "PRIMARY_RZONE_ID"; // Build payload ObjectMapper mapper = new ObjectMapper(); ObjectNode payload = mapper.createObjectNode(); payload.put("name", name); payload.put("description", description); payload.put("account_id", accountId); payload.put("primary_rzone_id", primaryRzoneId); // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/devicetrustmanager/api/v4/division")) .header("x-api-key", adminApiKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload))) .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp 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 adminApiKey = "ADMIN_API_KEY"; // Request details string name = "Example division name"; string description = "Example division description."; string accountId = "ACCOUNT_ID"; string primaryRzoneId = "PRIMARY_RZONE_ID"; // Build payload var payload = new JsonObject { ["name"] = name, ["description"] = description, ["account_id"] = accountId, ["primary_rzone_id"] = primaryRzoneId }; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", adminApiKey); var content = new StringContent( payload.ToJsonString(), Encoding.UTF8, "application/json" ); var response = await client.PostAsync($"{baseUrl}/devicetrustmanager/api/v4/division", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Set the body fields: - `name` Unique division name (must start and end with an alphanumeric character). - `description` Free-form description shown in the UI and audit logs. - `account_id` Your DigiCert ONE account ID (a valid UUID). - `primary_rzone_id` The rendezvous zone ID that will broker device communication for devices in this division. **Successful response (201 Created):** ```json { "success": { "code": "DIVISION_CREATED_200", "verbose": "Example division name created successfully" } } ``` This response carries only a success message — the new division's `id` is not returned. Retrieve it via a list query in Step 2 before continuing. ## Step 2: Get the division ID The create-division endpoint returns only a success message, not the new division's `id`. List divisions filtered by your `account_id` and the name you chose in Step 1 to retrieve its `id`, which you will pass as `division_id` to Steps 3 and 4. **Request:** **curl** ```bash curl -X GET "https://demo.one.digicert.com/devicetrustmanager/api/v4/division?account_id=ACCOUNT_ID&name=Example%20division%20name" \ -H "x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" \ -H "Content-Type: application/json" | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" ADMIN_API_KEY = "ADMIN_API_KEY" # Request details ACCOUNT_ID = "ACCOUNT_ID" DIVISION_NAME = "Example division name" response = requests.get( f"{BASE_URL}/devicetrustmanager/api/v4/division", headers={ "x-api-key": DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY, "Content-Type": "application/json" }, params={ "account_id": ACCOUNT_ID, "name": DIVISION_NAME } ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://demo.one.digicert.com"; String adminApiKey = "ADMIN_API_KEY"; // Request details String accountId = "ACCOUNT_ID"; String divisionName = "Example division name"; String query = "account_id=" + URLEncoder.encode(accountId, StandardCharsets.UTF_8) + "&name=" + URLEncoder.encode(divisionName, StandardCharsets.UTF_8); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/devicetrustmanager/api/v4/division?" + query)) .header("x-api-key", adminApiKey) .header("Content-Type", "application/json") .GET() .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp 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 adminApiKey = "ADMIN_API_KEY"; // Request details string accountId = "ACCOUNT_ID"; string divisionName = "Example division name"; string query = $"account_id={Uri.EscapeDataString(accountId)}" + $"&name={Uri.EscapeDataString(divisionName)}"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", adminApiKey); var response = await client.GetAsync($"{baseUrl}/devicetrustmanager/api/v4/division?{query}"); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Set the query parameters: - `account_id` Your DigiCert ONE account ID. Required to scope the search. - `name` Exact match on the division name from [Step 1](#step-1-create-a-division). Without this filter, the endpoint returns every division on the account. **Successful response (200 OK):** ```json { "total": 1, "limit": 10, "offset": 0, "pagination": false, "next": false, "records": [ { "created_on": "2026-05-11T07:07:13Z", "updated_on": "2026-05-11T07:07:13Z", "name": "Example division name", "description": "Example division description.", "id": "", "account": { "id": "" }, "status": "ACTIVE" } ] } ``` From the single entry in the `records` array, save the `id` value as `division_id`. You will use it in [Step 3](#step-3-create-a-bootstrap-certificate- policy) and [Step 4](#step-4-create-a-device-group-with-the-bootstrap-policy-attached) to scope the certificate policy and device group to this division. ## Step 3: Create a bootstrap certificate policy A bootstrap certificate policy defines how a device's first certificate is requested and issued. A bootstrap certificate (also called a *birth credential*) is a long-lived x.509 certificate that establishes the device's identity at provisioning and stays with the device throughout its lifecycle. Operational certificates, configured in a separate operational policy, are the short-lived credentials a device rotates for day-to-day work. Wiring policy creation into your provisioning pipeline means you can version policies as code and roll changes alongside firmware. **Request:** ```json { "certificate_policy": { "name": "Example bootstrap policy", "ica_id": "ICA_ID", "division_id": "DIVISION_ID", "certificate_profile_id": "CERT_PROFILE_ID", "certificate_management_methods": [ "SINGLE" ], "key_generation_option": "client_or_server_side", "key_generation_type": "RSA_2048", "single_cert_request_parameters": { "key_generation_option": "client_or_server_side", "key_type": "rsa_2048", "key_generation_allowed_to_change": false, "allow_to_use_pregenerated_keys": false, "private_key_format": "pem", "encryption_certificate_source": "provided", "uploaded_encryption_certificate": "CERTIFICATE_DATA", "allow_key_cache": false, "key_cache_validity_period_type": "days", "key_cache_validity_period": 3, "response_with_certificate_only": false, "split_certificate_response": true, "include_chain_option": "include_ica_and_root" } } } ``` **curl** ```bash curl -X POST "https://demo.one.digicert.com/devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy" \ -H "x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "certificate_policy": { "name": "Example bootstrap policy", "ica_id": "ICA_ID", "division_id": "DIVISION_ID", "certificate_profile_id": "CERT_PROFILE_ID", "certificate_management_methods": ["SINGLE"], "key_generation_option": "client_or_server_side", "key_generation_type": "RSA_2048", "single_cert_request_parameters": { "key_generation_option": "client_or_server_side", "key_type": "rsa_2048", "key_generation_allowed_to_change": false, "allow_to_use_pregenerated_keys": false, "private_key_format": "pem", "encryption_certificate_source": "provided", "uploaded_encryption_certificate": "CERTIFICATE_DATA", "allow_key_cache": false, "key_cache_validity_period_type": "days", "key_cache_validity_period": 3, "response_with_certificate_only": false, "split_certificate_response": true, "include_chain_option": "include_ica_and_root" } } }' | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" ADMIN_API_KEY = "ADMIN_API_KEY" # Request details POLICY_NAME = "Example bootstrap policy" ICA_ID = "ICA_ID" DIVISION_ID = "DIVISION_ID" CERT_PROFILE_ID = "CERT_PROFILE_ID" ENCRYPTION_CERTIFICATE = "CERTIFICATE_DATA" payload = { "certificate_policy": { "name": POLICY_NAME, "ica_id": ICA_ID, "division_id": DIVISION_ID, "certificate_profile_id": CERT_PROFILE_ID, "certificate_management_methods": ["SINGLE"], "key_generation_option": "client_or_server_side", "key_generation_type": "RSA_2048", "single_cert_request_parameters": { "key_generation_option": "client_or_server_side", "key_type": "rsa_2048", "key_generation_allowed_to_change": False, "allow_to_use_pregenerated_keys": False, "private_key_format": "pem", "encryption_certificate_source": "provided", "uploaded_encryption_certificate": ENCRYPTION_CERTIFICATE, "allow_key_cache": False, "key_cache_validity_period_type": "days", "key_cache_validity_period": 3, "response_with_certificate_only": False, "split_certificate_response": True, "include_chain_option": "include_ica_and_root", }, }, } response = requests.post( f"{BASE_URL}/devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy", headers={ "x-api-key": ADMIN_API_KEY, "Content-Type": "application/json" }, json=payload ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java 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.ArrayNode; 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 adminApiKey = "ADMIN_API_KEY"; // Request details String policyName = "Example bootstrap policy"; String icaId = "ICA_ID"; String divisionId = "DIVISION_ID"; String certProfileId = "CERT_PROFILE_ID"; String encryptionCertificate = "CERTIFICATE_DATA"; // Build payload ObjectMapper mapper = new ObjectMapper(); ObjectNode singleCertParams = mapper.createObjectNode(); singleCertParams.put("key_generation_option", "client_or_server_side"); singleCertParams.put("key_type", "rsa_2048"); singleCertParams.put("key_generation_allowed_to_change", false); singleCertParams.put("allow_to_use_pregenerated_keys", false); singleCertParams.put("private_key_format", "pem"); singleCertParams.put("encryption_certificate_source", "provided"); singleCertParams.put("uploaded_encryption_certificate", encryptionCertificate); singleCertParams.put("allow_key_cache", false); singleCertParams.put("key_cache_validity_period_type", "days"); singleCertParams.put("key_cache_validity_period", 3); singleCertParams.put("response_with_certificate_only", false); singleCertParams.put("split_certificate_response", true); singleCertParams.put("include_chain_option", "include_ica_and_root"); ObjectNode certificatePolicy = mapper.createObjectNode(); certificatePolicy.put("name", policyName); certificatePolicy.put("ica_id", icaId); certificatePolicy.put("division_id", divisionId); certificatePolicy.put("certificate_profile_id", certProfileId); ArrayNode methods = certificatePolicy.putArray("certificate_management_methods"); methods.add("SINGLE"); certificatePolicy.put("key_generation_option", "client_or_server_side"); certificatePolicy.put("key_generation_type", "RSA_2048"); certificatePolicy.set("single_cert_request_parameters", singleCertParams); ObjectNode payload = mapper.createObjectNode(); payload.set("certificate_policy", certificatePolicy); // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy")) .header("x-api-key", adminApiKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload))) .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp 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 adminApiKey = "ADMIN_API_KEY"; // Request details string policyName = "Example bootstrap policy"; string icaId = "ICA_ID"; string divisionId = "DIVISION_ID"; string certProfileId = "CERT_PROFILE_ID"; string encryptionCertificate = "CERTIFICATE_DATA"; // Build payload var payload = new JsonObject { ["certificate_policy"] = new JsonObject { ["name"] = policyName, ["ica_id"] = icaId, ["division_id"] = divisionId, ["certificate_profile_id"] = certProfileId, ["certificate_management_methods"] = new JsonArray { "SINGLE" }, ["key_generation_option"] = "client_or_server_side", ["key_generation_type"] = "RSA_2048", ["single_cert_request_parameters"] = new JsonObject { ["key_generation_option"] = "client_or_server_side", ["key_type"] = "rsa_2048", ["key_generation_allowed_to_change"] = false, ["allow_to_use_pregenerated_keys"] = false, ["private_key_format"] = "pem", ["encryption_certificate_source"] = "provided", ["uploaded_encryption_certificate"] = encryptionCertificate, ["allow_key_cache"] = false, ["key_cache_validity_period_type"] = "days", ["key_cache_validity_period"] = 3, ["response_with_certificate_only"] = false, ["split_certificate_response"] = true, ["include_chain_option"] = "include_ica_and_root" } } }; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", adminApiKey); var content = new StringContent( payload.ToJsonString(), Encoding.UTF8, "application/json" ); var response = await client.PostAsync($"{baseUrl}/devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` > **Important** > > Wrap the request body in a `certificate_policy` object. The inner `certificate_policy` object holds the fields described below. Sending the fields at the top level returns a 400. Set the inner fields: - `name` Friendly certificate policy name. - `ica_id` ID of the issuing CA from DigiCert Private CA that will sign certificates for this policy. - `division_id` The `division_id` from [Step 2](#step-2-get-the-division-id). - `certificate_profile_id` Certificate profile ID from DigiCert Private CA that shapes the issued certificate. - `certificate_management_methods` Array of certificate issuance methods. Allowed values: `SINGLE`, `BATCH`, `TRUST_EDGE`, `EST`, `SCEP`, `CMP_V2`, `ACME`. Use `["SINGLE"]` for one-at-a-time issuance during device registration. - `key_generation_option` One of `client_side`, `server_side`, or `client_or_server_side`. Use `client_or_server_side` to allow either client-supplied keys or server-side generation. - `key_generation_type` Default key type when the server generates the keypair. One of `RSA_2048`, `RSA_3072`, `RSA_4096`, `P_256`, `P_384`, `P_512`, `ED_25519`. Required when `key_generation_option` is `server_side` or `client_or_server_side`. - `single_cert_request_parameters` Object that configures per-certificate request behavior when `certificate_management_methods` includes `SINGLE`. The fields below apply only when the request asks for server-side keypair generation (the request omits `csr` and sets `server_side_key_gen: true`); when the device provides a CSR, the policy generates no private key and these fields are not exercised. Set them now so the policy is ready if a future request switches to server-side keygen. - `key_type` Lowercase form of the key algorithm used for server-side generation (for example, `rsa_2048`). - `private_key_format` Encoding the server uses when it returns a generated private key. Use `pem` for PEM output. - `encryption_certificate_source` How the server obtains the encryption certificate that wraps any returned private key. Use `provided` to supply your own in `uploaded_encryption_certificate`. Other supported modes use TLS session encryption or a PKCS12 password. - `uploaded_encryption_certificate` PEM-encoded encryption certificate the server uses to wrap server-generated private keys before returning them. The device — or whatever system installs the credential — must hold the matching private key to decrypt. Required when `encryption_certificate_source` is `provided`. - `include_chain_option` Controls how much of the issuance chain accompanies the certificate. Use `include_ica_and_root` to return the ICA and root alongside the leaf. **Successful response (201 Created):** ```json { "certificate_policy": { "name": "Example bootstrap policy", "certificate_management_methods": [ "SINGLE" ], "enrollment_methods": [ "SINGLE" ], "allowed_ip_addresses": [], "key_generation_type": "rsa_2048", "key_generation_allow_to_change": false, "key_generation_option": "client_or_server_side", "allow_pre_generated_keys": false, "ca_connector_type": "digicert_one", "assign_to_authcert_device": false, "allow_key_cache": false, "division_id": "", "require_approval_for_enroll": false, "require_approval_for_renew": false, "notification_email_list": [], "digest_email_notification_period": "do_not_send", "require_digital_signing": false, "new_batch_flow": true, "single_cert_request_parameters": { "key_generation_option": "client_or_server_side", "key_generation_allow_to_change": false, "key_type": "rsa_2048", "allow_to_use_pregenerated_keys": false, "private_key_format": "pem", "encryption_certificate_source": "provided", "uploaded_encryption_certificate": "", "allow_key_cache": false, "key_cache_validity_period_type": "days", "key_cache_validity_period": 3, "rsa_private_key_syntax": "pkcs8", "ecdsa_private_key_syntax": "pkcs8", "response_with_certificate_only": false, "split_certificate_response": true, "include_chain_option": "include_ica_and_root" }, "require_device_group_for_enroll": false, "migrated_to_new_params": false, "scep_get_ca_cert_response_with_root": false, "scep_get_ca_cert_response_der_format": false, "est_cacerts_include_full_tls_chain": false, "id": "", "certificate_profile": { "id": "", "name": "Example certificate profile" }, "created_at": "2026-05-04T19:14:55Z", "ica": { "id": "", "name": "Example Intermediate CA" }, "status": "ACTIVE", "allowed_signature_algorithms": [ "MLDSA-65" ], "direct_mapping": true, "account_id": "", "certificate_type": "end_entity", "approvers": [], "ca_body": "", "ca_chain": [ { "cert_type": "intermediate", "blob": "", "common_name": "Example intermediate CA" }, { "cert_type": "root", "blob": "", "common_name": "Example root CA" } ], "certificate_template": { "id": "", "name": "Example certificate template" }, "require_approval_for_revoke": false, "alternative_id": "" } } ``` From the response, save the certificate policy's `id` value as `certificate_policy_id`. You will use it in [Step 4](#step-4-create-a-device-group-with-the-bootstrap-policy-attached) when you attach the policy to the device group, and in [Step 6](#step-6-register-the-device) when you register the device against the policy. ## Step 4: Create a device group with the bootstrap policy attached A device group is a logical bucket of devices that share the same policies and assignment rules. Attaching the bootstrap certificate policy to the device group at creation time means every device registered to the group will use that policy automatically. That way registration becomes a single API call rather than a per-device policy assignment. **Request:** ```json { "name": "Example Device Group", "description": "Custom description", "is_static": true, "account_id": "ACCOUNT_ID", "division_id": "DIVISION_ID", "attributes": [ "mac_address" ], "policies": [ { "type": "bootstrapCertificate", "policy_id": "CERTIFICATE_POLICY_ID", "assignment_name": "Bootstrap policy assignment", "device_field_mappings": [ { "key": "mac_address", "field": "subject.common_name" } ], "status": "ACTIVE" } ] } ``` **curl** ```bash curl -X POST "https://demo.one.digicert.com/devicetrustmanager/api/v4/device-group" \ -H "x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Example Device Group", "description": "Custom description", "is_static": true, "account_id": "ACCOUNT_ID", "division_id": "DIVISION_ID", "attributes": ["mac_address"], "policies": [ { "type": "bootstrapCertificate", "policy_id": "CERTIFICATE_POLICY_ID", "assignment_name": "Bootstrap policy assignment", "device_field_mappings": [ { "key": "mac_address", "field": "subject.common_name" } ], "status": "ACTIVE" } ] }' | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" ADMIN_API_KEY = "ADMIN_API_KEY" # Request details NAME = "Example Device Group" DESCRIPTION = "Custom description" ACCOUNT_ID = "ACCOUNT_ID" DIVISION_ID = "DIVISION_ID" CERTIFICATE_POLICY_ID = "CERTIFICATE_POLICY_ID" payload = { "name": NAME, "description": DESCRIPTION, "is_static": True, "account_id": ACCOUNT_ID, "division_id": DIVISION_ID, "attributes": ["mac_address"], "policies": [ { "type": "bootstrapCertificate", "policy_id": CERTIFICATE_POLICY_ID, "assignment_name": "Bootstrap policy assignment", "device_field_mappings": [ {"key": "mac_address", "field": "subject.common_name"} ], "status": "ACTIVE", } ], } response = requests.post( f"{BASE_URL}/devicetrustmanager/api/v4/device-group", headers={ "x-api-key": ADMIN_API_KEY, "Content-Type": "application/json" }, json=payload ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java 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.ArrayNode; 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 adminApiKey = "ADMIN_API_KEY"; // Request details String name = "Example Device Group"; String description = "Custom description"; String accountId = "ACCOUNT_ID"; String divisionId = "DIVISION_ID"; String certificatePolicyId = "CERTIFICATE_POLICY_ID"; // Build payload ObjectMapper mapper = new ObjectMapper(); ObjectNode payload = mapper.createObjectNode(); payload.put("name", name); payload.put("description", description); payload.put("is_static", true); payload.put("account_id", accountId); payload.put("division_id", divisionId); ArrayNode attributesArray = payload.putArray("attributes"); attributesArray.add("mac_address"); ObjectNode fieldMapping = mapper.createObjectNode(); fieldMapping.put("key", "mac_address"); fieldMapping.put("field", "subject.common_name"); ObjectNode policy = mapper.createObjectNode(); policy.put("type", "bootstrapCertificate"); policy.put("policy_id", certificatePolicyId); policy.put("assignment_name", "Bootstrap policy assignment"); policy.putArray("device_field_mappings").add(fieldMapping); policy.put("status", "ACTIVE"); payload.putArray("policies").add(policy); // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/devicetrustmanager/api/v4/device-group")) .header("x-api-key", adminApiKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload))) .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp 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 adminApiKey = "ADMIN_API_KEY"; // Request details string name = "Example Device Group"; string description = "Custom description"; string accountId = "ACCOUNT_ID"; string divisionId = "DIVISION_ID"; string certificatePolicyId = "CERTIFICATE_POLICY_ID"; // Build payload var payload = new JsonObject { ["name"] = name, ["description"] = description, ["is_static"] = true, ["account_id"] = accountId, ["division_id"] = divisionId, ["attributes"] = new JsonArray { "mac_address" }, ["policies"] = new JsonArray { new JsonObject { ["type"] = "bootstrapCertificate", ["policy_id"] = certificatePolicyId, ["assignment_name"] = "Bootstrap policy assignment", ["device_field_mappings"] = new JsonArray { new JsonObject { ["key"] = "mac_address", ["field"] = "subject.common_name" } }, ["status"] = "ACTIVE" } } }; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", adminApiKey); var content = new StringContent( payload.ToJsonString(), Encoding.UTF8, "application/json" ); var response = await client.PostAsync($"{baseUrl}/devicetrustmanager/api/v4/device-group", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Set the body fields: - `name` Device group name (must start and end with an alphanumeric character). - `description` Free-form description. - `is_static` Use `true` for a manually managed group. Set to `false` for a dynamic group whose membership is driven by attribute-matching rules. - `account_id` Account ID for device association. - `division_id` The `division_id` from [Step 2](#step-2-get-the-division-id). - `attributes` Inventory attribute values that uniquely identify devices in this group. For example, `["mac_address"]`. - `policies` An array describing policies attached to the group. For a bootstrap-only group, include one entry shaped like the example below: ```json { "type": "bootstrapCertificate", "policy_id": "", "assignment_name": "Bootstrap policy assignment", "device_field_mappings": [ { "key": "mac_address", "field": "subject.common_name" } ], "status": "ACTIVE" } ``` `type` must be `bootstrapCertificate` (or `operationalCertificate` for an operational policy). `device_field_mappings` ties an inventory attribute (`key`) to a certificate subject field (`field`). > **Note** > > Confirm subject field naming convention by querying `GET /devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy/{PolicyID}/enrollment-specification` for the chosen policy. The exact valid `field` values are policy-dependent. **Successful response (201 Created):** ```json { "success": { "code": "DEVICE_GROUP_CREATED_200", "verbose": "Device group created" } } ``` Like the create-division endpoint in Step 1, this response carries only a success message — the new device group's `id` is not returned. Retrieve it via a list query in Step 5 before registering the device. ## Step 5: Get the device group ID The create-device-group endpoint returns only a success message, not the new device group's `id`. List device groups filtered by your `account_id` and the name you chose in Step 4 to retrieve its `id`, which you will pass as `device_group_id` to Step 6. **Request:** **curl** ```bash curl -X GET "https://demo.one.digicert.com/devicetrustmanager/api/v4/device-group?account_id=ACCOUNT_ID&name=Example%20Device%20Group" \ -H "x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" \ -H "Content-Type: application/json" | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" ADMIN_API_KEY = "ADMIN_API_KEY" # Request details ACCOUNT_ID = "ACCOUNT_ID" DEVICE_GROUP_NAME = "Example Device Group" response = requests.get( f"{BASE_URL}/devicetrustmanager/api/v4/device-group", headers={ "x-api-key": ADMIN_API_KEY, "Content-Type": "application/json" }, params={ "account_id": ACCOUNT_ID, "name": DEVICE_GROUP_NAME } ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://demo.one.digicert.com"; String adminApiKey = "ADMIN_API_KEY"; // Request details String accountId = "ACCOUNT_ID"; String deviceGroupName = "Example Device Group"; String query = "account_id=" + URLEncoder.encode(accountId, StandardCharsets.UTF_8) + "&name=" + URLEncoder.encode(deviceGroupName, StandardCharsets.UTF_8); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/devicetrustmanager/api/v4/device-group?" + query)) .header("x-api-key", adminApiKey) .header("Content-Type", "application/json") .GET() .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp 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 adminApiKey = "ADMIN_API_KEY"; // Request details string accountId = "ACCOUNT_ID"; string deviceGroupName = "Example Device Group"; string query = $"account_id={Uri.EscapeDataString(accountId)}" + $"&name={Uri.EscapeDataString(deviceGroupName)}"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", adminApiKey); var response = await client.GetAsync($"{baseUrl}/devicetrustmanager/api/v4/device-group?{query}"); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Set the query parameters: - `account_id` Your DigiCert ONE account ID. Required to scope the search. - `name` Exact match on the device group name from [Step 4](#step-4-create-a-device-group-with-the-bootstrap-policy-attached). Without this filter, the endpoint returns every device group on the account. **Successful response (200 OK):** ```json { "total": 1, "limit": 10, "offset": 0, "pagination": false, "next": false, "records": [ { "created_on": "2026-05-11T07:14:55Z", "updated_on": "2026-05-11T07:14:55Z", "name": "Example Device Group", "description": "Custom description", "id": "", "account_id": "", "division_id": "", "is_static": true, "status": "ACTIVE" } ] } ``` From the single entry in the `records` array, save the `id` value as `device_group_id`. You will use it in Step 6 to register the device against this group. ## Step 6: Register the device With the division, certificate policy, and device group in place, you can register the device. Registration creates the device record, ties it to the group's bootstrap policy, and triggers issuance of the device's first certificate using the CSR or public key you provide. **Request:** ```json { "name": "Example Device", "description": "Example Device description", "device_group_id": "DEVICE_GROUP_ID", "account_id": "ACCOUNT_ID", "certificate_policies": { "bootstrap": [ { "csr": "BOOTSTRAP_CSR", "certificate_policy_id": "BOOTSTRAP_POLICY_ID", "attributes": [ { "name": "subject.common_name", "value": "device-001" } ] } ] } } ``` **curl** ```bash curl -X POST "https://demo.one.digicert.com/devicetrustmanager/api/v4/device/registration" \ -H "x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Example Device", "description": "Example Device description", "device_group_id": "DEVICE_GROUP_ID", "account_id": "ACCOUNT_ID", "certificate_policies": { "bootstrap": [ { "csr": "BOOTSTRAP_CSR", "certificate_policy_id": "BOOTSTRAP_POLICY_ID", "attributes": [ { "name": "subject.common_name", "value": "device-001" } ] } ] } }' | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" ADMIN_API_KEY = "ADMIN_API_KEY" # Request details DEVICE_NAME = "Example Device" DEVICE_DESCRIPTION = "Example Device description" DEVICE_GROUP_ID = "DEVICE_GROUP_ID" ACCOUNT_ID = "ACCOUNT_ID" BOOTSTRAP_POLICY_ID = "BOOTSTRAP_POLICY_ID" BOOTSTRAP_CSR = "BOOTSTRAP_CSR" payload = { "name": DEVICE_NAME, "description": DEVICE_DESCRIPTION, "device_group_id": DEVICE_GROUP_ID, "account_id": ACCOUNT_ID, "certificate_policies": { "bootstrap": [ { "csr": BOOTSTRAP_CSR, "certificate_policy_id": BOOTSTRAP_POLICY_ID, "attributes": [ {"name": "subject.common_name", "value": "device-001"} ], } ], }, } response = requests.post( f"{BASE_URL}/devicetrustmanager/api/v4/device/registration", headers={ "x-api-key": ADMIN_API_KEY, "Content-Type": "application/json" }, json=payload ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java 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.ArrayNode; 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 adminApiKey = "ADMIN_API_KEY"; // Request details String deviceName = "Example Device"; String deviceDescription = "Example Device description"; String deviceGroupId = "DEVICE_GROUP_ID"; String accountId = "ACCOUNT_ID"; String bootstrapPolicyId = "BOOTSTRAP_POLICY_ID"; String bootstrapCsr = "BOOTSTRAP_CSR"; // Build payload ObjectMapper mapper = new ObjectMapper(); ObjectNode payload = mapper.createObjectNode(); payload.put("name", deviceName); payload.put("description", deviceDescription); payload.put("device_group_id", deviceGroupId); payload.put("account_id", accountId); ObjectNode bootstrapAttribute = mapper.createObjectNode(); bootstrapAttribute.put("name", "subject.common_name"); bootstrapAttribute.put("value", "device-001"); ObjectNode bootstrapEntry = mapper.createObjectNode(); bootstrapEntry.put("csr", bootstrapCsr); bootstrapEntry.put("certificate_policy_id", bootstrapPolicyId); bootstrapEntry.putArray("attributes").add(bootstrapAttribute); ObjectNode certificatePolicies = mapper.createObjectNode(); certificatePolicies.putArray("bootstrap").add(bootstrapEntry); payload.set("certificate_policies", certificatePolicies); // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/devicetrustmanager/api/v4/device/registration")) .header("x-api-key", adminApiKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload))) .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp 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 adminApiKey = "ADMIN_API_KEY"; // Request details string deviceName = "Example Device"; string deviceDescription = "Example Device description"; string deviceGroupId = "DEVICE_GROUP_ID"; string accountId = "ACCOUNT_ID"; string bootstrapPolicyId = "BOOTSTRAP_POLICY_ID"; string bootstrapCsr = "BOOTSTRAP_CSR"; // Build payload var payload = new JsonObject { ["name"] = deviceName, ["description"] = deviceDescription, ["device_group_id"] = deviceGroupId, ["account_id"] = accountId, ["certificate_policies"] = new JsonObject { ["bootstrap"] = new JsonArray { new JsonObject { ["csr"] = bootstrapCsr, ["certificate_policy_id"] = bootstrapPolicyId, ["attributes"] = new JsonArray { new JsonObject { ["name"] = "subject.common_name", ["value"] = "device-001" } } } } } }; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", adminApiKey); var content = new StringContent( payload.ToJsonString(), Encoding.UTF8, "application/json" ); var response = await client.PostAsync($"{baseUrl}/devicetrustmanager/api/v4/device/registration", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Set the body fields: - `name` The device name (must be unique within the device group). - `description` Free-form description. - `device_group_id` The `device_group_id` from [Step 5](#step-5-get-the-device-group-id). - `account_id` Your DigiCert ONE account ID. - `certificate_policies` An object containing a `bootstrap` array. Each array entry is shaped like the example below: ```json { "certificate_policy_id": "", "attributes": [ { "name": "subject.common_name", "value": "device-001" } ], "csr": "" } ``` Each `attributes` entry maps a subject field name (`name`) to a value (`value`). The endpoint also accepts an `operational` array with the same shape for issuing the device's first operational certificate at registration. Creating an operational policy and using that array is covered in [What's next?](#whats-next). **Successful response (201 Created):** ```json { "device_id": "", "device_name": "Example Device", "created_on": 1777923541028, "connected": false, "description": "Example Device description", "device_status": "REGISTERED", "operational_status": "ENABLED", "private_keys": [ { "policy_id": "", "policy_type": "BOOTSTRAP", "certificate": "" } ] } ``` From the response, save the device's `device_id` value. The `private_keys[].certificate` field carries the issued bootstrap certificate, which the device must install to complete onboarding. > **Note** > > Your device holds the private key for this certificate. The request you sent supplied a CSR carrying the public half of a keypair the device generated locally, so the server only needed to issue and return the certificate. That is the client-side path, and your Step 3 policy supports it alongside server-side generation because you set `key_generation_option` to `client_or_server_side`. > > If you switch to the server-side path by omitting the `csr` from the registration request, the response also includes a `private_keys[].private_key` field. Keep that value secure. It is the device's identity, and anything holding it can impersonate the device. The policy’s `single_cert_request_parameters block` from [Step 3](https://dev.digicert.com/md/device-trust-api/tutorials/register-a-single-device.md#step-3-create-a-bootstrap-certificate-policy) controls how the server encrypts the key before returning it; with the TLS session, with the encryption certificate you uploaded, or as a password-protected PKCS12 file. Decrypt it on the receiving system, hand it to the device over a confidential channel, and remove every server-side copy once the device confirms installation. ## Common errors and solutions For general API errors (authentication, rate limits), see [Error handling and rate limits](https://dev.digicert.com/md/get-started/error-handling-rate-limits.md). ### Invalid name ```json { "errors": [ { "code": "03", "message": "Name must start with a letter and end with an alphanumeric character. Accepts spaces, special chars (,.;:_-) and simple balanced parentheses." } ] } ``` The `name` field on division, certificate policy, device group, and device must start with a letter and end with an alphanumeric character. Trailing or leading special characters (`-`, `.`, `;`, `:`, `_`, `,`) cause this error. Strip whitespace and review the name format before retrying. ### Invalid UUID ```json { "errors": [ { "code": "03", "message": "Account id should be a valid UUID." } ] } ``` The `account_id`, `division_id`, `certificate_policy_id`, `ica_id`, `certificate_profile_id`, and `device_group_id` fields must be valid UUIDs. This error commonly appears when copying values from a UI that wraps IDs in display formatting. Confirm each ID is a bare UUID with no surrounding quotes, brackets, or whitespace. ### Missing required field ```json { "errors": [ { "code": "missing_required_field", "message": "Name cannot be empty or null or just spaces." } ] } ``` The POST endpoints in this tutorial reject empty or whitespace-only required fields. Audit the request body against the required-field list at the top of each step before retrying. ## What's next? Now that the device is registered with a bootstrap certificate, you can: - **Register devices in batch**: Use the batch device registration endpoint to onboard many devices at once from a CSV. - **Issue an operational certificate**: Create an operational certificate policy and attach it to the device group. Devices authenticate with their long-lived bootstrap credential to request short-lived operational certificates for day-to-day work. - **Add an authentication policy**: Configure mutual TLS or passcode-based authentication on the device registration endpoint so devices can re-authenticate without an admin API key. - **Monitor device activity**: Wire device events into your monitoring stack. Events such as failed enrollments, certificate issuance, and policy changes are surfaced in Device Trust Manager audit logs.