> ## Documentation Index
> Fetch the complete documentation index at: https://docs.promptguard.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Issue Device Certificate

> Issue this device its Shadow Device certificate, or return its live one again.

A short-lived, name-constrained CA under the Organization's active
Organisation root, for the device key in `csr`, with the root's empty
revocation list for exactly its life (Shadow ADR-0016). Refused once the
Device is revoked (ADR-0007). The contract, every refusal and what the
agent does on each: `docs/04_engineering/reference/SHADOW_DEVICE_CERTIFICATES.md`.

Guide: [Enroll your team's devices](https://docs.promptguard.co/shadow-ai/fleet-enrollment)



## OpenAPI

````yaml /api-reference/openapi-developer.json post /api/v1/fleet/device-certificate
openapi: 3.1.0
info:
  title: PromptGuard Developer API
  description: >-
    Public API for developers to integrate PromptGuard security into their
    applications
  version: 1.0.0
servers:
  - url: https://api.promptguard.co
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /api/v1/fleet/device-certificate:
    post:
      tags:
        - fleet-device
      summary: Issue Device Certificate
      description: >-
        Issue this device its Shadow Device certificate, or return its live one
        again.


        A short-lived, name-constrained CA under the Organization's active

        Organisation root, for the device key in `csr`, with the root's empty

        revocation list for exactly its life (Shadow ADR-0016). Refused once the

        Device is revoked (ADR-0007). The contract, every refusal and what the

        agent does on each:
        `docs/04_engineering/reference/SHADOW_DEVICE_CERTIFICATES.md`.


        Guide: [Enroll your team's
        devices](https://docs.promptguard.co/shadow-ai/fleet-enrollment)
      operationId: issue_device_certificate_api_v1_fleet_device_certificate_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeviceCertificateRequest'
        required: true
      responses:
        '200':
          description: 'The live certificate again: its CSR was sent before'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceCertificateResponse'
              example:
                certificate: |
                  -----BEGIN CERTIFICATE-----
                  MIIC...
                  -----END CERTIFICATE-----
                serial: 4c1d9f0b7a2e5c3d8e6f1a0b9c8d7e6f5a4b3c2d
                not_before: '2026-11-14T09:00:00Z'
                not_after: '2026-11-21T10:00:00Z'
                renew_after: '2026-11-17T21:30:00Z'
                permitted_dns_names:
                  - api.anthropic.com
                  - api.openai.com
                  - claude.ai
                root:
                  id: 6a0f2a8e-3c47-4f0e-9b4a-2d6c1e8f7a35
                  certificate: |
                    -----BEGIN CERTIFICATE-----
                    MIIB...
                    -----END CERTIFICATE-----
                  fingerprint_sha256: >-
                    9f2c4b7d1e3a5c8f0b2d4e6a8c0e2f4a6b8d0f2a4c6e8b0d2f4a6c8e0b2d4f6a
                revocation_list:
                  crl: MIIBKjCB0QIBATAKBggqhkjOPQQDAjA9...
                  this_update: '2026-11-14T09:00:00Z'
                  next_update: '2026-11-21T10:00:00Z'
                  distribution_point: http://127.0.0.1:47811/pgshadow-org-3f9a1c0e5b7d2a64.crl
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceCertificateResponse'
        '401':
          description: Missing, malformed, unknown, disabled or expired API key
          content:
            application/json:
              examples:
                missing_api_key:
                  summary: No X-API-Key header
                  value:
                    error:
                      message: >-
                        PromptGuard API key required. Please provide via
                        X-API-Key header.
                      type: authentication_error
                      code: missing_api_key
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#missing_api_key
                malformed_api_key:
                  summary: Not a pg_live_ key
                  value:
                    error:
                      message: Invalid API key format
                      type: authentication_error
                      code: invalid_api_key
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#invalid_api_key
                unknown_api_key:
                  summary: No such key
                  value:
                    error:
                      message: Invalid API key
                      type: authentication_error
                      code: invalid_api_key
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#invalid_api_key
                disabled_api_key:
                  summary: Key disabled
                  value:
                    error:
                      message: API key is disabled
                      type: authentication_error
                      code: disabled_api_key
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#disabled_api_key
                expired_api_key:
                  summary: Key expired
                  value:
                    error:
                      message: API key has expired
                      type: authentication_error
                      code: expired_api_key
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#expired_api_key
              schema:
                $ref: '#/components/schemas/AuthErrorEnvelope'
        '403':
          description: Refused by the key's scopes or the organization's IP allowlist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                scope_denied:
                  summary: The key's scopes exclude this endpoint
                  value:
                    error:
                      message: >-
                        This endpoint needs the `shadow:write` scope, which this
                        API key does not hold. Create a key that includes it, or
                        use an unrestricted key.
                      type: insufficient_scope
                      code: scope_denied
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#scope_denied
                scan_only_credential:
                  summary: A scan-only device credential
                  value:
                    error:
                      message: This credential is scan-only; endpoint not permitted.
                      type: insufficient_scope
                      code: scope_denied
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#scope_denied
                read_only_key:
                  summary: A SIEM export key
                  value:
                    error:
                      message: >-
                        This is a read-only export key. It may only GET
                        /api/v1/alerts, /api/v1/audit-events,
                        /api/v1/audit-events/stream, /api/v1/interactions and
                        /api/v1/interactions/stream.
                      type: insufficient_scope
                      code: read_only_key
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#read_only_key
                ip_not_allowed:
                  summary: Caller IP outside the allowlist
                  value:
                    error:
                      message: Request IP is not in the organization's allowlist
                      type: permission_error
                      code: ip_not_allowed
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#ip_not_allowed
                ip_allowlist_error:
                  summary: Allowlist could not be checked
                  value:
                    error:
                      message: IP allowlist validation failed
                      type: permission_error
                      code: ip_allowlist_error
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#ip_allowlist_error
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorEnvelope'
        '429':
          description: Per-minute rate limit reached; retry after `retry_after` seconds
          content:
            application/json:
              examples:
                rate_limit_exceeded:
                  summary: Per-minute limit reached
                  value:
                    error:
                      message: >-
                        Rate limit exceeded: your plan allows 60 requests per
                        minute. Please retry later.
                      type: rate_limit_exceeded
                      code: rate_limit_exceeded
                      retry_after: 42
                      doc_url: >-
                        https://docs.promptguard.co/production/error-handling#rate_limit_exceeded
              schema:
                $ref: '#/components/schemas/RateLimitErrorEnvelope'
      externalDocs:
        description: 'Guide: Enroll your team''s devices'
        url: https://docs.promptguard.co/shadow-ai/fleet-enrollment
components:
  schemas:
    DeviceCertificateRequest:
      properties:
        csr:
          type: string
          title: Csr
          description: >-
            PEM PKCS#10 for an EC P-256 key, signed by that key with
            ECDSA-SHA256. Only its public key is used.
        key_protection:
          type: string
          enum:
            - secure_enclave
            - tpm
            - software
          title: Key Protection
          description: 'Where the private key lives: secure_enclave | tpm | software'
        os_user:
          type: string
          maxLength: 128
          minLength: 1
          pattern: ^[^\x00-\x1f\x7f]+$
          title: Os User
          description: >-
            The OS account the key belongs to: the Windows SID or the macOS
            user's UUID
        crl_distribution_point:
          type: string
          maxLength: 160
          title: Crl Distribution Point
          description: >-
            Where the agent serves the returned revocation list:
            http://127.0.0.1:<port>/<name>.crl or http://[::1]:<port>/<name>.crl
      additionalProperties: false
      type: object
      required:
        - csr
        - key_protection
        - os_user
        - crl_distribution_point
      title: DeviceCertificateRequest
      description: >-
        A Device asks for its certificate. Unknown fields are refused, not
        ignored.
    DeviceCertificateResponse:
      properties:
        certificate:
          type: string
          title: Certificate
          description: The Device certificate, PEM. The authority.
        serial:
          type: string
          title: Serial
          description: Lowercase hex, no separators
        not_before:
          type: string
          format: date-time
          title: Not Before
        not_after:
          type: string
          format: date-time
          title: Not After
        renew_after:
          type: string
          format: date-time
          title: Renew After
          description: Issue time plus half the validity
        permitted_dns_names:
          items:
            type: string
          type: array
          title: Permitted Dns Names
          description: The certificate's permitted dNSName subtrees, as it carries them
        root:
          $ref: '#/components/schemas/DeviceCertificateRoot'
        revocation_list:
          $ref: '#/components/schemas/DeviceCertificateRevocationList'
      type: object
      required:
        - certificate
        - serial
        - not_before
        - not_after
        - renew_after
        - permitted_dns_names
        - root
        - revocation_list
      title: DeviceCertificateResponse
      description: >-
        A Device certificate, the root it chains to and the list that says it is
        not revoked.
    AuthErrorEnvelope:
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
          examples:
            - code: missing_api_key
              message: >-
                PromptGuard API key required. Please provide via X-API-Key
                header.
              type: authentication_error
      type: object
      required:
        - error
      title: AuthErrorEnvelope
    ErrorEnvelope:
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
      type: object
      required:
        - error
      title: ErrorEnvelope
    ValidationErrorEnvelope:
      description: The developer API's 422 (`shared/infrastructure/framework_errors.py`).
      properties:
        error:
          $ref: '#/components/schemas/ValidationErrorDetail'
        detail:
          deprecated: true
          description: >-
            The same list as `error.details`, in FastAPI's old shape. Removed on
            2027-02-11; read `error.details`
          items:
            $ref: '#/components/schemas/ValidationErrorField'
          title: Detail
          type: array
      required:
        - error
        - detail
      title: ValidationErrorEnvelope
      type: object
      example:
        error:
          message: 'The request is invalid: body.messages: Field required'
          type: invalid_request_error
          code: invalid_request
          details:
            - type: missing
              loc:
                - body
                - messages
              msg: Field required
              input:
                direction: input
          error_id: 3f0b8c1e-6a0e-4d52-9a57-0c2a9a7f5b11
          doc_url: >-
            https://docs.promptguard.co/production/error-handling#invalid_request
        detail:
          - type: missing
            loc:
              - body
              - messages
            msg: Field required
            input:
              direction: input
    RateLimitErrorEnvelope:
      properties:
        error:
          $ref: '#/components/schemas/RateLimitErrorDetail'
      type: object
      required:
        - error
      title: RateLimitErrorEnvelope
    DeviceCertificateRoot:
      properties:
        id:
          type: string
          format: uuid
          title: Id
        certificate:
          type: string
          title: Certificate
          description: The active Organisation root, PEM
        fingerprint_sha256:
          type: string
          title: Fingerprint Sha256
          description: SHA-256 of the root's DER, lowercase hex, no separators
      type: object
      required:
        - id
        - certificate
        - fingerprint_sha256
      title: DeviceCertificateRoot
    DeviceCertificateRevocationList:
      properties:
        crl:
          type: string
          title: Crl
          description: The root's empty revocation list, base64 of the DER
        this_update:
          type: string
          format: date-time
          title: This Update
        next_update:
          type: string
          format: date-time
          title: Next Update
        distribution_point:
          type: string
          title: Distribution Point
          description: The request's crl_distribution_point
      type: object
      required:
        - crl
        - this_update
        - next_update
        - distribution_point
      title: DeviceCertificateRevocationList
    ErrorDetail:
      properties:
        message:
          type: string
          title: Message
          description: Human-readable error description
        type:
          type: string
          title: Type
          description: Error category, e.g. 'authentication_error'
        code:
          type: string
          title: Code
          description: Machine-readable error code
        doc_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Doc Url
          description: >-
            The error catalogue's entry for `code` (cause and fix); present when
            the catalogue documents the code
      type: object
      required:
        - message
        - type
        - code
      title: ErrorDetail
    ValidationErrorDetail:
      properties:
        message:
          description: The failed fields, one line
          title: Message
          type: string
        type:
          description: '''invalid_request_error'''
          title: Type
          type: string
        code:
          description: '''invalid_request'''
          title: Code
          type: string
        details:
          description: Each field that failed
          items:
            $ref: '#/components/schemas/ValidationErrorField'
          title: Details
          type: array
        error_id:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: Equals the X-Request-ID header
          title: Error Id
        doc_url:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          description: >-
            The error catalogue's entry for `code` (cause and fix); present when
            the catalogue documents the code
          title: Doc Url
      required:
        - message
        - type
        - code
        - details
      title: ValidationErrorDetail
      type: object
    ValidationErrorField:
      description: One field that failed validation, as FastAPI reports it.
      properties:
        loc:
          description: The field's path, e.g. ['body', 'messages']
          items:
            anyOf:
              - type: string
              - type: integer
          title: Loc
          type: array
        msg:
          description: What is wrong with it
          title: Msg
          type: string
        type:
          description: The kind of failure, e.g. 'missing'
          title: Type
          type: string
        input:
          default: null
          description: The value that was sent
          title: Input
        ctx:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          default: null
          description: The limit it broke, where there is one
          title: Ctx
      required:
        - loc
        - msg
        - type
      title: ValidationErrorField
      type: object
    RateLimitErrorDetail:
      properties:
        message:
          type: string
          title: Message
        type:
          type: string
          title: Type
          description: '''rate_limit_exceeded'''
        code:
          type: string
          title: Code
          description: '''rate_limit_exceeded'''
        retry_after:
          type: integer
          title: Retry After
          description: Seconds until the window resets; also `Retry-After`
        doc_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Doc Url
          description: >-
            The error catalogue's entry for `code` (cause and fix); present when
            the catalogue documents the code
      type: object
      required:
        - message
        - type
        - code
        - retry_after
      title: RateLimitErrorDetail
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        PromptGuard API key for developer endpoints. Keys start with pg_live_
        and are created in the dashboard.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.