--- title: "Register multiple devices" description: "Register multiple devices and issue each device a bootstrap certificate through one asynchronous batch job.\n" source_url: https://dev.digicert.com/device-trust-api/tutorials/register-multiple-devices.html --- A batch job is ideal when all devices use the same certificate management policy. Instead of sending individual requests, you submit one request that processes all devices at once. In this tutorial, you will: - Create a division to scope your devices and policies. - Retrieve the division's ID for use in later steps. - Work with templates and certificate profiles to create a certificate management policy and issue bootstrap certificates to multiple devices - Create a device group and attach the bootstrap certificate management policy - Retrieve the device group's ID for use in registration - Register multiple devices 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` to [create a certificate management policy](#step-3-create-a-certificate-management-policy-to-issue-a-bootstrap-certificate). - An existing certificate profile - A rendezvous zone configured in Device Trust Manager. You will need its `primary_rzone_id` for [creating a division](#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). Device Trust Manager includes several default resources that are automatically created during provisioning including `device rendezvous zones`. - 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 will present this while creating a certificate management policy. ## Default resources Device Trust Manager includes several default resources that are automatically created during provisioning. These resources provide a starting point for configuring device management and certificate lifecycle operations. - `Issuing CA`: You'll use this when creating a certificate management policy. It serves as the certificate authority that issues and signs device certificates - `Certificate templates`: You'll use this when requesting or issuing certificates (in this tutorial, you are requesting a bootstrap certificate). It defines the certificate settings and attributes applied to your devices. - `Device rendezvous zones`: You'll use this during device onboarding. It provides the location information devices need to connect to Device Trust Manager. ## 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 | | GET | `/devicetrustmanager/certificate-configuration-service/api/v1/certificate-template` | Get the certificate template ID | POST | `/devicetrustmanager/certificate-configuration-service/api/v1/certificate-profile` | Create a certificate profile | POST | `/devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy` | Create a certificate management policy to issue a bootstrap certificate | | POST | `/devicetrustmanager/api/v4/device-group` | Create a device group with the bootstrap policy attached | | GET | `/devicetrustmanager/api/v4/device-group` | List device groups to retrieve the device group ID | | GET | `/devicetrustmanager/api/v4/device-group` | Get the device group ID | POST | `/devicetrustmanager/api/v4/device/registration` | Register the devices | ## 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` | [Create a certificate management policy to issue a bootstrap certificate](#step-3-create-a-certificate-management-policy-to-issue-a-bootstrap-certificate) | `SINGLE`, `BATCH`, `TRUST_EDGE`, `EST`, `SCEP`, `CMP_V2`, `ACME` | | `key_generation_option` | [Create a certificate management policy to issue a bootstrap certificate](#step-3-create-a-certificate-management-policy-to-issue-a-bootstrap-certificate) | `client_side`, `server_side`, `client_or_server_side` | | `key_generation_type` | [Create a certificate management policy to issue a bootstrap certificate](#step-3-create-a-certificate-management-policy-to-issue-a-bootstrap-certificate) | `RSA_2048`, `RSA_3072`, `RSA_4096`, `P_256`, `P_384`, `P_512`, `ED_25519` | | `certificate-template` | [Get the certificate template ID](#step-3a-get-the-certificate-template-id) | `end_entity`, `intermediate` | | `certificate-profile` | [Create a certificate profile](#step-3b-create-a-certificate-profile) | `csr`, `certificate_value`, `unique_certificate_value`, `signature_algorithm`, `allow_any_key_type`, `allowed_key_types`, `subject.common_name` | ## 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:** ```bash curl --request POST \ --url https://demo.one.digicert.com/devicetrustmanager/api/v4/division \ --header 'x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY' \ --data '{ "account_id": "ACCOUNT_ID", "name": "Example division name", "description": "Example division description", "primary_rzone_id": "PRIMARY_RZONE_ID", "user_ids": [] }' ``` **Set the body fields**: - `account_id`: Your DigiCert ONE account ID (a valid UUID) - `name`: Unique division name (must start and end with an alphanumeric character) - `description`: Free-form description shown in the UI and audit logs - `primary_rzone_id`: A rendezvous zone configured in Device Trust Manager. This rendezvous zone ID will broker device communication for devices in this division **Successful response (200 OK):** ```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 [Get the division ID](#step-2-get-the-division-id) before continuing. ## Step 2: Get the division ID The create a division endpoint returns a success message, but does not return the **new division's ID**. To get the newly created division ID, list the divisions using your `account_id` and the division name you created in [Create a division](#step-1-create-a-division). Use the returned **ID** as the `division_id` for [creating a certificate management policy](#step-3-create-a-certificate-management-policy-to-issue-a-bootstrap-certificate) as well as for [creating a device group](#step-4-create-a-device-group-with-the-bootstrap-policy-attached). **Request:** ```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 '.' ``` **Set the query parameters**: - `account_id`: Your DigiCert ONE account ID. Required to scope the search. - `name`: Exact match on the division name where you [created a division](#step-1-create-a-division). Without this filter, the endpoint returns every division associated with your account. **Successful response (200 OK):** ```json { "limit": 10, "offset": 0, "pagination": true, "next": false, "total": 1, "records": [ { "created_on": "2026-08-31T09:49:18Z", "updated_on": "2026-08-31T09:49:18Z", "name": "Example division name", "description": "Example division description", "id": "DIVISION_ID", "account": { "id": "ACCOUNT_ID", "name": "ACCOUNT_NAME" }, "status": "ACTIVE", "is_migrated": false } ] } ``` Save the `records[0].id` value as `division_id`. You will use it when creating a [certificate management policy](#step-3-create-a-certificate-management-policy-to-issue-a-bootstrap-certificate) and when [attaching a bootstrap policy to the device group](#step-4-create-a-device-group-with-the-bootstrap-policy-attached) to scope the certificate policy and device group to the division. ## Step 3: Create a certificate management policy to issue a bootstrap certificate Creating a certificate management policy involves adding a **Certificate template**, creating a **Certificate profile**, and proceeding to create a **Certificate management policy**. For this tutorial, you are going to: - Select an existing `certificate template` (custom certificate template available in your account) - Create a `certificate profile` - Create a `certificate management policy` ### Step 3a: Get the certificate template ID Certificate templates define the certificate fields and constraints available when you create a certificate profile. Templates are provided as part of your onboarding. List the templates available to your account and retrieve the ID of the template you want to use for the devices' certificate management policy. > **Info** > > Certificate profiles and policies can be created only from custom (non-system) certificate templates. When listing templates, filter the results using `type=custom` to ensure that only eligible templates are returned. **Request:** ```bash curl -X GET "https://demo.one.digicert.com/devicetrustmanager/certificate-configuration-service/api/v1/certificate-template?account_id=ACCOUNT_ID&type=custom&name=CERTIFICATE_TEMPLATE_NAME&status=ACTIVE&format=x509" \ -H "x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY" \ -H "Content-Type: application/json" | jq '.' ``` **Set the query parameters**: - `account_id`: Your DigiCert ONE account ID - `type`: Use **custom** to return only customer-created templates that can be used to create certificate profiles - `name`: URL-encoded name of the certificate template provided during onboarding - `status`: Use `ACTIVE` to exclude disabled and deleted templates - `format`: Use `x509` for the bootstrap certificate workflow in this tutorial **Successful response (200 OK):** ```json { "limit": 20, "offset": 0, "pagination": true, "next": false, "total": 1, "records": [ { "id": "CERTIFICATE_TEMPLATE_ID", "name": "CERTIFICATE_TEMPLATE_NAME", "status": "ACTIVE", "type": "custom", "format": "x509", "certificate_type": "end_entity" } ] } ``` Confirm that the selected template is **ACTIVE** and that its type is **custom**. Save its id value as `CERTIFICATE_TEMPLATE_ID`. You will use it while [creating a certificate profile](#step-3b-create-a-certificate-profile). ### Step 3b: Create a certificate profile A certificate profile configures the certificate fields that Device Trust Manager uses when issuing bootstrap certificates. Create the certificate profile from the certificate template you selected in [Get the certificate template ID](#step-3a-get-the-certificate-template-id) and assign it to the division you created in [Create a division](#step-1-create-a-division). This tutorial enables a CSR and allows the common name to come from either the CSR or the batch registration data. **Request:** ```bash curl --request POST \ --url https://demo.one.digicert.com/devicetrustmanager/certificate-configuration-service/api/v1/certificate-profile \ --header 'x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY' \ --data '{ "account_id": "ID of the account the certificate profile belongs to (UUID)", "name": "Example batch bootstrap certificate profile", "ca_connector_type": "digicert_one", "certificate_template_id": "ID of the certificate template to use in the certificate profile", "divisions": [ "DIVISION_ID" ], "omit_primitive_encoding": false, "body": [ { "key": "signature_algorithm", "optional": false, "enabled": true, "sources": [ "fixed_value" ], "value": "match_issuer" }, { "key": "subject.common_name", "optional": true, "enabled": true, "sources": [ "user_supplied", "csr" ], "value": "" }, { "key": "subject.organization_name", "optional": true, "enabled": false, "sources": [], "value": "" }, { "key": "subject.organization_unit", "optional": true, "enabled": false, "sources": [], "value": [] }, { "key": "subject.street_address", "optional": true, "enabled": false, "sources": [], "value": [] }, { "key": "subject.postal_code", "optional": true, "enabled": false, "sources": [], "value": "" }, { "key": "subject.locality", "optional": true, "enabled": false, "sources": [], "value": "" }, { "key": "subject.state", "optional": true, "enabled": false, "sources": [], "value": "" }, { "key": "subject.country", "optional": true, "enabled": false, "sources": [], "value": "" }, { "key": "subject.email", "optional": true, "enabled": false, "sources": [], "value": "" }, { "key": "key_usage.critical", "optional": true, "enabled": true, "sources": [ "fixed_value" ], "value": "yes" }, { "key": "key_usage.dilithium_additional_values", "optional": true, "enabled": false, "sources": [ "user_supplied", "csr" ], "value": [] }, { "key": "key_usage.sphincs_additional_values", "optional": true, "enabled": false, "sources": [ "user_supplied", "csr" ], "value": [] }, { "key": "extended_key_usage.critical", "optional": true, "enabled": true, "sources": [ "fixed_value" ], "value": "yes" }, { "key": "csr", "optional": false, "enabled": true, "sources": [ "user_supplied" ], "value": null }, { "key": "certificate_value", "optional": false, "enabled": true, "sources": [ "fixed_value" ], "value": "auto" }, { "key": "unique_certificate_value", "optional": false, "enabled": true, "sources": [ "fixed_value" ], "value": "no" }, { "key": "validity.duration_unit", "optional": false, "enabled": true, "sources": [ "fixed_value" ], "value": "years" }, { "key": "validity.duration_value", "optional": false, "enabled": true, "sources": [ "fixed_value" ], "value": 10 }, { "key": "allow_any_key_type", "optional": false, "enabled": true, "sources": [ "fixed_value" ], "value": "yes" }, { "key": "allowed_key_types", "optional": false, "enabled": true, "sources": [ "fixed_value" ], "value": [ "rsa_1024", "rsa_2048", "rsa_3072", "rsa_4096", "p_256", "p_384", "p_521", "ed_25519", "MLDSA-44", "MLDSA-65", "MLDSA-87", "SLHDSA-SHAKE-128f", "SLHDSA-SHA2-128s", "SLHDSA-SHAKE-128s", "SLHDSA-SHA2-128f", "SLHDSA-SHAKE-192s", "SLHDSA-SHA2-192f", "SLHDSA-SHA2-192s", "SLHDSA-SHAKE-192f", "SLHDSA-SHAKE-256f", "SLHDSA-SHA2-256s", "SLHDSA-SHAKE-256s", "SLHDSA-SHA2-256f" ] }, { "optional": false, "enabled": true, "sources": [ "fixed_value" ], "key": "renewal_settings.renew_valid_cert", "value": "anytime" }, { "optional": false, "enabled": true, "sources": [ "fixed_value" ], "key": "renewal_settings.renew_expired_cert", "value": "anytime" }, { "optional": false, "enabled": true, "sources": [ "fixed_value" ], "key": "renewal_settings.renewal_key_pair", "value": "optional" }, { "optional": false, "enabled": true, "sources": [ "fixed_value" ], "key": "renewal_settings.renew_revoked_cert", "value": true } ], "certificate_type": "end_entity" }' ``` **Set the body fields**: - `name`: Unique certificate profile name - `account_id`: Account that owns the profile - `certificate_template_id`: Template ID [Get the certificate template ID](#step-3a-get-the-certificate-template-id) - `body`: Certificate field configuration. Every entry requires `key`, `optional`, `enabled`, `sources`, and `value` - `divisions`: Division IDs where the profile is available. Use `DIVISION_ID` from [Get the division ID](#step-2-get-the-division-id) **Successful response (201 Created):** ```json { "id": "CERTIFICATE_PROFILE_ACCOUNT_ID", "name": "Example certificate profile name", "account_id": "ACCOUNT_ID", "certificate_template": { "id": "CERTIFICATE_TEMPLATE_ID>", "name": "CERTIFICATE_TEMPLATE_NAME" }, "allow_bare_public_key": false, "created_at": "2026-09-03T06:40:04Z", "status": "ACTIVE", "body": [ { "key": "signature_algorithm", "optional": false, "sources": [ "fixed_value" ], "value": "match_issuer", "enabled": true }, { "key": "subject.common_name", "optional": true, "sources": [ "user_supplied", "csr" ], "enabled": true }, { "key": "subject.organization_name", "optional": true, "sources": [], "enabled": false }, { "key": "subject.organization_unit", "optional": true, "sources": [], "enabled": false }, { "key": "subject.street_address", "optional": true, "sources": [], "enabled": false }, { "key": "subject.postal_code", "optional": true, "sources": [], "enabled": false }, { "key": "subject.locality", "optional": true, "sources": [], "enabled": false }, { "key": "subject.state", "optional": true, "sources": [], "enabled": false }, { "key": "subject.country", "optional": true, "sources": [], "enabled": false }, { "key": "subject.email", "optional": true, "sources": [], "enabled": false }, { "key": "key_usage.critical", "optional": true, "sources": [ "fixed_value" ], "value": "yes", "enabled": true }, { "key": "key_usage.dilithium_additional_values", "optional": true, "sources": [ "user_supplied", "csr" ], "enabled": false }, { "key": "key_usage.sphincs_additional_values", "optional": true, "sources": [ "user_supplied", "csr" ], "enabled": false }, { "key": "extended_key_usage.critical", "optional": true, "sources": [ "fixed_value" ], "value": "yes", "enabled": true }, { "key": "certificate_value", "optional": false, "sources": [ "fixed_value" ], "value": "auto", "enabled": true }, { "key": "unique_certificate_value", "optional": false, "sources": [ "fixed_value" ], "value": "no", "enabled": true }, { "key": "validity.duration_unit", "optional": false, "sources": [ "fixed_value" ], "value": "years", "enabled": true }, { "key": "validity.duration_value", "optional": false, "sources": [ "fixed_value" ], "value": 10, "enabled": true }, { "key": "allow_any_key_type", "optional": false, "sources": [ "fixed_value" ], "value": "yes", "enabled": true }, { "key": "allowed_key_types", "optional": false, "sources": [ "fixed_value" ], "value": [ "rsa_1024", "rsa_2048", "rsa_3072", "rsa_4096", "p_256", "p_384", "p_521", "ed_25519", "MLDSA-44", "MLDSA-65", "MLDSA-87", "SLHDSA-SHAKE-128f", "SLHDSA-SHA2-128s", "SLHDSA-SHAKE-128s", "SLHDSA-SHA2-128f", "SLHDSA-SHAKE-192s", "SLHDSA-SHA2-192f", "SLHDSA-SHA2-192s", "SLHDSA-SHAKE-192f", "SLHDSA-SHAKE-256f", "SLHDSA-SHA2-256s", "SLHDSA-SHAKE-256s", "SLHDSA-SHA2-256f" ], "enabled": true }, { "key": "renewal_settings.renew_valid_cert", "optional": false, "sources": [ "fixed_value" ], "value": "anytime", "enabled": true }, { "key": "renewal_settings.renew_expired_cert", "optional": false, "sources": [ "fixed_value" ], "value": "anytime", "enabled": true }, { "key": "renewal_settings.renewal_key_pair", "optional": false, "sources": [ "fixed_value" ], "value": "optional", "enabled": true }, { "key": "renewal_settings.renew_revoked_cert", "optional": false, "sources": [ "fixed_value" ], "value": true, "enabled": true } ], "enrollment_methods": [], "certificate_format": "x509", "certificate_type": "end_entity", "ca_connector_type": "digicert_one", "omit_primitive_encoding": false, "divisions": [ { "id": "DIVISION_ID", "name": "Example division name" } ], "is_migrated": false, "editable": true, "non_limited_by_division_allowed": true, "server_side_key_generation_allowed": true } ``` ### Step 3c: Create a certificate management policy A certificate management policy defines how certificates are issued, renewed, and revoked for devices. It outlines the protocols for certificate requests with the use of certificate templates, certificate profiles, and issuing CAs. **Request:** ```bash curl --request POST \ --url https://demo.one.digicert.com/devicetrustmanager/certificate-configuration-service/api/v2/certificate-policy \ --header 'x-api-key: DEVCTM_SOLUTION_ADMINISTRATOR_API_KEY' \ --data '{ "certificate_policy": { "certificate_management_methods": [ "BATCH" ], "batch_request_parameters": { "encryption_certificate_source": "provided", "output_format": "zip", "certificate_format": "pem", "report_format": "csv", "include_chain_option": "include_ica_and_root", "allow_external_download": false, "rsa_private_key_syntax": "PKCS8", "ecdsa_private_key_syntax": "PKCS8", "passcode_generation_option": "NONE" }, "name": "Example bootstrap policy", "ica_id": "ICA_ID", "created_at": "2026-09-02T07:58:17.495Z", "certificate_profile_id": "CERT_PROFILE_ID", "division_id": "DIVISION_ID", "require_approval_for_enroll": false, "key_generation_option": "server_side", "key_generation_type": "rsa_2048", "allowed_ip_addresses": [], "labels": [] } }' ``` **Set the body fields**: - `certificate_management_methods`: Array of certificate issuance methods. Allowed values: `SINGLE`, `BATCH`, `TRUST_EDGE`, `EST`, `SCEP`, `CMP_V2`, `ACME`. Use `BATCH` for registering multiple devices. - `encryption_certificate_source`: How the server obtains the encryption certificate that wraps any returned private key. - `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. - `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 [Get the division ID](#step-2-get-the-division-id) - `certificate_profile_id`: Certificate profile ID from DigiCert Private CA that shapes the issued certificate - `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` **Successful response (201 Created):** ```json { "certificate_policy": { "name": "Example bootstrap policy", "certificate_management_methods": [ "BATCH" ], "enrollment_methods": [ "BATCH" ], "allowed_ip_addresses": [], "key_generation_type": "rsa_2048", "key_generation_allow_to_change": false, "key_generation_option": "server_side", "allow_pre_generated_keys": false, "ca_connector_type": "digicert_one", "assign_to_authcert_device": false, "allow_key_cache": false, "division_id": "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, "batch_request_parameters": { "key_generation_option": "client_side", "key_generation_allow_to_change": false, "allow_to_use_pregenerated_keys": false, "encryption_certificate_source": "provided", "output_format": "zip", "certificate_format": "pem", "rsa_private_key_syntax": "pkcs8", "ecdsa_private_key_syntax": "pkcs8", "include_chain_option": "include_ica_and_root", "report_format": "CSV", "match_private_key_extension_with_certificate": false, "allow_external_download": false, "passcode_generation_option": "none" }, "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, "labels": [], "is_migrated": false, "id": "IOT_ID", "certificate_profile": { "id": "certificate_profile_ID", "name": "certificate_profile_name" }, "created_at": "2026-09-02T09:39:43Z", "ica": { "id": "", "name": "Example Intermediate CA name" }, "status": "ACTIVE", "allowed_signature_algorithms": [ "sha256WithRSA" ], "direct_mapping": true, "account_id": "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": "CERTIFICATE_TEMPLATE_ID", "name": "name of the certificate template" }, "require_approval_for_revoke": false, "alternative_id": "" } } ``` From the response, save the `certificate_policy.id` value as `certificate_policy_id`. You will use it while [creating a device group](#step-4-create-a-device-group-with-the-bootstrap-policy-attached), when you attach the policy to the device group, and when you [register the devices](#step-6-register-the-devices) against the certificate management 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:** ```bash curl --location 'https://demo.one.digicert.com/devicetrustmanager/api/v4/device-group' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'x-api-key: ' \ --data '{ "name": "Example Device Group", "description": "Custom description", "is_static": true, "account_id": "ACCOUNT_ID", "division_id": "DIVISION_ID", "labels": [], "attributes": [ "device_name" ], "users": [], "auth_policy_id": "", "policies": [ { "type": "bootstrapCertificate", "policy_id": "CERTIFICATE_POLICY_ID", "assignment_name": "Bootstrap policy assignment", "auth_policy_id": "", "device_field_mappings": [ { "key": "device_name", "field": "csr" } ], "time_stamp": 1788344828, "status": "ACTIVE" } ], "desired_attributes": [] }' ``` **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, `["device_name"]` - `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": "certificate_policy_id", "assignment_name": "Bootstrap policy assignment", "device_field_mappings": [ { "key": "device_name", "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 (200 OK):** ```json { "success": { "code": "DEVICE_GROUP_CREATED_200", "verbose": "Device group created" } } ``` Like the [create-division endpoint](#step-1-create-a-division), this response carries only a success message—the new device group's `id` is not returned. Retrieve it via a list query using ([Get the device group ID](#step-5-get-the-device-group-id)) 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 while [creating a device group](#step-4-create-a-device-group-with-the-bootstrap-policy-attached) to retrieve its `id`, which you will pass as `device_group_id` while [registering the devices](#step-6-register-the-devices). **Request:** ```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 '.' ``` **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": "", "name": "Example division name" }, "is_static": true, "attributes": [ "device_name" ], "labels": [], "device_attribute_definitions": [], "status": "ACTIVE" } ] } ``` From the single entry in the `records` array, save the `id` value as `device_group_id`. You will use it while [registering the devices](#step-6-register-the-devices) to register the device against this group. ## Step 6: Register the devices With the division, certificate policy, and device group in place, you can register the devices. 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:** ```bash curl -X GET https://demo.one.digicert.com/devicetrustmanager/certificate-batch-service/api/v1/jobs?job_type=device_registration \ --header 'Accept: application/json' \ --header 'Content-Type: multipart/form-data' ``` **Successful response (200 OK):** ```json { "limit": 20, "offset": 0, "pagination": true, "next": true, "total": 633, "records": [ { "id": "BATCH_ENROLLMENT_JOB_ID", "account_id": "ACCOUNT_ID", "division_id": "DIVISION_ID", "name": "Example batch job name", "job_type": "device_registration", "certificate_policy_id": "CERTIFICATE_POLICY_ID", "device_group_id": "DEVICE_GROUP_ID", "metadata": { "emails": [ "Comma-separated list of email addresses for users who receive a notification email when the batch enrollment job is complete" ], "passcode_regenerated": false, "server_side_key_gen": false, "bootstrap_config_template": "{\"configuration\":{\"device_id\":\"${deviceId}\",\"account_id\":\"ACCOUNT_ID\",\"division_id\":\"DIVISION_ID\",\"device_group_id\":\"DEVICE_GROUP_ID\",\"rendezvous_configuration\":{\"mqtt_endpoint\":{\"primary\":[\"mqtt://MQTT_RENDEZVOUS_ZONE_URL\"]},\"persist_connection\":false}}}", "client_ip": "IP_ADDRESS", "rzone_cas": [] }, "status": "failed", "result": "failure", "created_at": "2026-09-02T10:55:38Z", "expire_at": "2026-10-02T10:55:39Z", "total_items": 14, "failed_items": 14, "processing_time": 602, "retry_count": 0, "data_available": true, "error_report_available": false, "completed_at": "2026-09-02T10:55:39Z" }, { "id": "BATCH_ENROLLMENT_JOB_ID", "account_id": "ACCOUNT_ID", "division_id": "DIVISION_ID", "name": "Example batch job name", "job_type": "device_registration", "certificate_policy_id": "CERTIFICATE_POLICY_ID", "device_group_id": "DEVICE_GROUP_ID", "metadata": { "emails": [ "Comma-separated list of email addresses for users who receive a notification email when the batch enrollment job is complete" ], "passcode_regenerated": false, "server_side_key_gen": true, "key_type": "rsa_2048", "encryption_certificate": "The PEM-encoded X.509 certificate used to encrypt sensitive data or payloads with CERTIFICATE_DATA", "bootstrap_config_template": "{\"configuration\":{\"device_id\":\"${deviceId}\",\"account_id\":\"ACCOUNT_ID\",\"division_id\":\"DIVISION_ID\",\"device_group_id\":\"DEVICE_GROUP_ID\",\"rendezvous_configuration\":{\"mqtt_endpoint\":{\"primary\":[\"mqtt://MQTT_RENDEZVOUS_ZONE_URL\"]},\"persist_connection\":false}}}", "client_ip": "IP_ADDRESS", "rzone_cas": [] }, "status": "completed", "result": "success", "created_at": "2026-09-01T11:05:34Z", "expire_at": "2026-10-01T11:05:42Z", "total_items": 3, "failed_items": 0, "processing_time": 7822, "retry_count": 0, "data_available": true, "error_report_available": false, "completed_at": "2026-09-01T11:05:42Z" }, { } ] } ``` 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 you have registered multiple devices with a bootstrap certificate, you may want to: - **Register a single device**. See [Register a single device](https://dev.digicert.com/md/device-trust-api/tutorials/register-a-single-device.md) - **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.