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

# Duplicate template into signing request

> Creates a new signing request by duplicating an existing template, including all fields, users, reminders, and settings.

**Seals.** The new signing request carries the template's seal participants and the template fields that belong to them. This operation takes no `seal_participants` key. While multi-document editing is suspended, templates on the multi-document read-only path answer 409 `MULTI_DOCUMENT_READ_ONLY`. For templates on the single-document path during that suspension, a template seal follows the current version of its lineage: if that current version is revoked or deleted, or the lineage has no current version, the call answers 409 `SEAL_UNAVAILABLE` and creates nothing; a seal that the API key's company cannot see, or that is limited to a workspace other than the target workspace, is not copied and neither are its fields, and the 201 lists it in `seal_participants` as a dropped entry; if a seal would be kept while organization seals are not enabled, the call answers 503 `SEALS_DISABLED`; the 201 always carries `seal_participants` (an empty array when the template has no seal participants). For every company during normal operation, the copy attaches the template's stored seal version as it is, the 201 does not carry `seal_participants`, and a seal that was replaced, revoked or deleted since the template was saved fails when the request is sent.



## OpenAPI

````yaml api-reference/v01.39.01/openapi-v01.39.01.json post /templates/{id}/duplicate
openapi: 3.0.3
info:
  title: Firma Partner API
  description: >-
    RESTful API for document signing and template management.


    **Authentication**: All endpoints require API key authentication via the
    `Authorization` header. Use your API key directly without any prefix (e.g.,
    `your-api-key`). The Bearer prefix is optional but not required.


    **Security Features**:

    - Input validation using Zod schemas with detailed field-level error
    messages

    - RSA-256 signed JWT tokens for embedded template access


    **Rate Limiting**: Rate limits are tiered based on operation type:

    - Read operations (GET): 200 requests per minute

    - Write operations (POST/PUT/PATCH/DELETE): 120 requests per minute

    - Webhook CRUD operations: 60 requests per minute

    - Webhook test: 10 requests per minute

    - API key regeneration/expiration: 1 request per minute

    - Webhook secret rotation: 1 request per minute


    When rate limits are exceeded, the API returns a `429 Too Many Requests`
    response with headers:

    - `X-RateLimit-Limit`: Maximum requests per minute for this endpoint

    - `X-RateLimit-Remaining`: Requests remaining in current window

    - `X-RateLimit-Reset`: Unix timestamp when limit resets

    - `Retry-After`: Seconds until retry is allowed


    **Error Handling**: All errors return structured JSON responses with `error`
    (human-readable message), `code` (machine-readable identifier), and
    `details` (field-level validation errors when applicable).


    **Embedded Template Integration**: The Firma Template Editor can be embedded
    in your application using a standalone JavaScript library.


    ```html

    <!-- Load the Firma Template Editor library -->

    <script
    src="https://api.firma.dev/functions/v1/embed-proxy/template-editor.js"></script>


    <script>

    // Generate JWT token via API first

    fetch('https://api.firma.dev/functions/v1/signing-request-api/generate-template-token',
    {
      method: 'POST',
      headers: {
        'Authorization': 'YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        companies_workspaces_templates_id: 'template-id'
      })
    })

    .then(res => res.json())

    .then(data => {
      // Initialize editor with JWT token
      window.FirmaTemplateEditor.init({
        container: '#firma-editor-container',
        jwt: data.token,
        templateId: 'template-id',
        theme: 'dark',
        readOnly: false,
        onSave: (savedData) => {
          console.log('Template saved:', savedData);
        },
        onError: (error) => {
          console.error('Editor error:', error);
        },
        onLoad: (template) => {
          console.log('Template loaded:', template);
        }
      });
    });

    ```


    **X-Firma-Deprecation Header**: Some seal creation and image update
    operations return an `X-Firma-Deprecation` response header when called on
    the edge gateway, indicating the operation should be performed on the
    primary API host instead.


    **Multi-document documents (API 01.39.00, available to every company)**: a
    template or signing request can hold up to 20 documents. The `/documents`
    sub-resource routes and the `documents`, `documents_version` and
    `documents_layout_version` keys are served for every company. While
    multi-document editing is suspended, parents on the multi-document read-only
    path keep their single-resource reads and document downloads (a template can
    still be deleted), but other edits answer `409 MULTI_DOCUMENT_READ_ONLY`;
    parents on the single-document path have no `/documents` routes (`404
    NOT_FOUND`) or multi-document response keys, and list and create responses
    omit those keys.


    **Template seals on create (API 01.39.01)**: creating a signing request from
    a template with `POST /signing-requests` or `POST
    /signing-requests/create-and-send` now inherits the template's seal
    participants, and the template fields that belong to them, when
    `seal_participants` is absent or `null`. This changes the behaviour of v1
    without a version bump; send `seal_participants: []` to create the request
    with no seals. `POST /templates/{id}/duplicate` also carries the template's
    seal participants over; see that operation for how it handles seal versions
    and suspended multi-document editing. `GET /templates/{id}` returns
    `seal_participants`, and `POST /templates` answers `warnings` when its body
    contains keys it ignores.
  version: 01.39.01
  contact:
    name: API Support
    url: https://firma.com/support
servers:
  - url: https://api.firma.dev/functions/v1/signing-request-api
    description: Production API - Recommended (Current)
  - url: https://api.firma.dev/api/v1
    description: Production API - Planned
security:
  - ApiKeyAuth: []
tags:
  - name: Company
    description: Company information and settings
  - name: Workspaces
    description: Workspace management operations
  - name: Templates
    description: Template management operations
  - name: Signing Requests
    description: Document signing request operations
  - name: Custom Fields
    description: >-
      Custom field definition management for workspaces, templates, and signing
      requests
  - name: Webhooks
    description: Webhook configuration and management
  - name: JWT Management
    description: JWT token generation and revocation for embedded templates
  - name: Workspace Settings
    description: Workspace configuration and settings
  - name: Email Domains
    description: >-
      Email domain setup and verification for sending signing request emails
      from custom domains
  - name: Email Templates
    description: >-
      Email template management for workspace and company-level customization of
      signing request notifications
  - name: Organization Seals
    description: >-
      Organization seal management: create, update, revoke, and erase seals
      applied to signing requests
  - name: Signer Terms
    description: >-
      Custom signer terms-of-service / consent statements, company-level with
      per-language workspace overrides
paths:
  /templates/{id}/duplicate:
    post:
      tags:
        - Templates
      summary: Duplicate template into signing request
      description: >-
        Creates a new signing request by duplicating an existing template,
        including all fields, users, reminders, and settings.


        **Seals.** The new signing request carries the template's seal
        participants and the template fields that belong to them. This operation
        takes no `seal_participants` key. While multi-document editing is
        suspended, templates on the multi-document read-only path answer 409
        `MULTI_DOCUMENT_READ_ONLY`. For templates on the single-document path
        during that suspension, a template seal follows the current version of
        its lineage: if that current version is revoked or deleted, or the
        lineage has no current version, the call answers 409 `SEAL_UNAVAILABLE`
        and creates nothing; a seal that the API key's company cannot see, or
        that is limited to a workspace other than the target workspace, is not
        copied and neither are its fields, and the 201 lists it in
        `seal_participants` as a dropped entry; if a seal would be kept while
        organization seals are not enabled, the call answers 503
        `SEALS_DISABLED`; the 201 always carries `seal_participants` (an empty
        array when the template has no seal participants). For every company
        during normal operation, the copy attaches the template's stored seal
        version as it is, the 201 does not carry `seal_participants`, and a seal
        that was replaced, revoked or deleted since the template was saved fails
        when the request is sent.
      operationId: duplicateTemplate
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Template ID to duplicate
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 255
                  description: >-
                    Custom name for the new signing request (defaults to
                    template name)
            example:
              name: Q4 2025 Contract
      responses:
        '201':
          description: Template duplicated successfully into a new signing request
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: 'Rate limit: 120 requests per minute'
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TemplateDuplicateResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          description: Access to the source template or the target workspace was denied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          description: >-
            Conflict: `SEAL_UNAVAILABLE`, `MULTI_DOCUMENT_READ_ONLY`,
            `DOCUMENT_SET_BUSY`, `DOCUMENT_SET_INCONSISTENT`,
            `DOCUMENT_SET_VERSION_CONFLICT`, `DOCUMENT_SET_DISPATCH_STALE`
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/MultiDocumentError'
                  - allOf:
                      - $ref: '#/components/schemas/Error'
                      - type: object
                        required:
                          - error
                          - code
                        properties:
                          code:
                            type: string
                            enum:
                              - SEAL_UNAVAILABLE
        '429':
          $ref: '#/components/responses/RateLimitError'
        '503':
          description: >-
            Organization seals are not enabled and the template has a seal
            participant that would be kept (`SEALS_DISABLED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: TypeScript
          label: '@firma-dev/sdk'
          source: |-
            import { FirmaClient } from "@firma-dev/sdk";

            const firma = new FirmaClient({ apiKey: "YOUR_API_KEY" });

            const response = await firma.templates.duplicateTemplate({
                id: "id",
                name: "Q4 2025 Contract"
            });
            console.log(response);
components:
  schemas:
    TemplateDuplicateResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: ID of the newly created signing request
        name:
          type: string
          description: Name of the new signing request
        description:
          type: string
          nullable: true
          description: Description of the new signing request
        status:
          type: string
          description: Status of the new signing request
          example: draft
        workspace_id:
          type: string
          format: uuid
          description: Workspace the signing request belongs to
        created_date:
          type: string
          format: date-time
          description: Creation timestamp
        seal_participants:
          type: array
          description: >-
            Seal participants of the new signing request, persisted entries (the
            shape of the create operations, except that `order` can be null)
            with `dropped: false`, then dropped entries. Not answered during
            normal operation for any company. While multi-document editing is
            suspended, duplication of a template on the multi-document read-only
            path answers 409 `MULTI_DOCUMENT_READ_ONLY`; on the single-document
            path, this key is always present, an empty array when the template
            has no seal participants.
          items:
            oneOf:
              - $ref: '#/components/schemas/TemplateDuplicateSealParticipant'
              - $ref: '#/components/schemas/SealParticipantDropped'
      description: Duplicated template details
      required:
        - id
        - name
        - status
        - workspace_id
    Error:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message
        code:
          type: string
          description: >-
            Machine-readable error code Seal-related codes: SEALS_DISABLED,
            SEAL_ALREADY_REVOKED, SEAL_CREATION_DISABLED_EDGE,
            SEAL_ERASE_NOT_ELIGIBLE, SEAL_IMAGE_INVALID,
            SEAL_MUTATION_NOT_ALLOWED, SEAL_NOT_FOUND, SEAL_NO_IMAGE_FIELD,
            SEAL_ORDER_COLLISION, SEAL_PAUSED, SEAL_SCOPE_FORBIDDEN,
            SEAL_UNAVAILABLE Multi-document codes: see MultiDocumentErrorCode
            (41 codes).
        errors:
          type: array
          description: >-
            All validation errors when multiple failures are reported together.
            The top-level error repeats the first item for backward
            compatibility.
          items:
            type: object
            required:
              - message
            properties:
              message:
                type: string
        message:
          type: string
          description: Detailed error description
        details:
          type: object
          description: Additional error details
          additionalProperties: true
      required:
        - error
      description: >-


        Organization Seal error codes: SEALS_DISABLED, SEAL_ALREADY_REVOKED,
        SEAL_CREATION_DISABLED_EDGE, SEAL_ERASE_NOT_ELIGIBLE,
        SEAL_IMAGE_INVALID, SEAL_MUTATION_NOT_ALLOWED, SEAL_NOT_FOUND,
        SEAL_NO_IMAGE_FIELD, SEAL_ORDER_COLLISION, SEAL_PAUSED,
        SEAL_SCOPE_FORBIDDEN, SEAL_UNAVAILABLE
    MultiDocumentError:
      description: An error answered by a multi-document route.
      allOf:
        - $ref: '#/components/schemas/Error'
        - type: object
          required:
            - error
            - code
          properties:
            code:
              $ref: '#/components/schemas/MultiDocumentErrorCode'
    TemplateDuplicateSealParticipant:
      type: object
      description: >-
        A persisted seal participant of the duplicated signing request: every
        key of SealParticipantApi, plus `dropped: false`. `order` is normally an
        integer and is null only if the position was not reported when the
        participant was saved.
      required:
        - id
        - order
        - seal
        - status
        - applied_at
        - paused_at
        - pause_reason
        - swapped_from_seal_id
        - triggered_by_recipient_id
        - sender_kind
        - seal_out_of_scope
        - dropped
      properties:
        id:
          type: string
          format: uuid
        order:
          type: integer
          minimum: 1
          nullable: true
          description: >-
            Position in the signing order, shared with the recipients' order
            space. Normally an integer. Null only if the position was not
            reported when the participant was saved; read the signing request to
            get it.
        seal:
          $ref: '#/components/schemas/SealSummary'
        status:
          type: string
          enum:
            - pending
            - paused
            - applied
          description: >-
            applied when applied_at is set, else paused when paused_at is set,
            else pending.
        applied_at:
          type: string
          format: date-time
          nullable: true
        paused_at:
          type: string
          format: date-time
          nullable: true
        pause_reason:
          type: string
          nullable: true
          enum:
            - seal_revoked_stop_pending
            - seal_integrity_mismatch
            - null
        swapped_from_seal_id:
          type: string
          format: uuid
          nullable: true
        triggered_by_recipient_id:
          type: string
          format: uuid
          nullable: true
          description: The recipient whose action applied the seal.
        sender_kind:
          type: string
          nullable: true
          enum:
            - dashboard
            - api_key
            - embedded
            - null
        seal_out_of_scope:
          type: boolean
          description: >-
            True when the seal belongs to a workspace other than the signing
            request's or the template's.
        dropped:
          type: boolean
          enum:
            - false
    SealParticipantDropped:
      type: object
      description: >-
        A template seal that was not copied to the new signing request or
        template because it is not available there: the key's company cannot see
        the seal, or the seal is limited to a workspace other than the target
        workspace. The fields that belong to the seal were not copied either.
        `seal` is the summary of the seal's current version, or null when the
        seal is not visible to the key's company.
      required:
        - id
        - order
        - seal
        - seal_out_of_scope
        - dropped
      properties:
        id:
          type: string
          nullable: true
          enum:
            - null
          description: 'Always null: no participant was created.'
        order:
          type: integer
          nullable: true
          enum:
            - null
          description: 'Always null: no participant was created.'
        seal:
          type: object
          nullable: true
          description: >-
            Summary of the seal's current version; null when the API key's
            company cannot see the seal.
          required:
            - id
            - lineage_id
            - name
            - version
            - display_name
          properties:
            id:
              type: string
              format: uuid
            lineage_id:
              type: string
              format: uuid
              description: Shared across versions of the same seal
            name:
              type: string
            version:
              type: integer
            display_name:
              type: string
        seal_out_of_scope:
          type: boolean
          enum:
            - true
        dropped:
          type: boolean
          enum:
            - true
    MultiDocumentErrorCode:
      type: string
      enum:
        - ALREADY_CANCELLED
        - ALREADY_FINISHED
        - ALREADY_SENT
        - ANCHOR_DOCUMENT_REQUIRED
        - ANCHOR_MISMATCH
        - CONFLICTING_DOCUMENT_SOURCE
        - DIMENSION_MISMATCH
        - DOCUMENTS_LAYOUT_VERSION_REQUIRED
        - DOCUMENT_CANDIDATE_EXPIRED
        - DOCUMENT_NOT_FOUND
        - DOCUMENT_ORIGINAL_NOT_AVAILABLE
        - DOCUMENT_SET_BUSY
        - DOCUMENT_SET_DISPATCH_STALE
        - DOCUMENT_SET_INCONSISTENT
        - DOCUMENT_SET_LOCKED
        - DOCUMENT_SET_TOO_LARGE
        - DOCUMENT_SET_VERSION_CONFLICT
        - DOCUMENT_SIGNED_NOT_AVAILABLE
        - DOCUMENT_UPLOAD_NOT_FOUND
        - DUPLICATE_DOCUMENT_ID
        - FENCE_COLUMN_PROTECTED
        - INSUFFICIENT_CREDITS
        - INTERNAL
        - INTERNAL_ERROR
        - INVALID_DOCUMENT_ORDER
        - INVALID_SIGNING_ORDER
        - MAX_DOCUMENTS_EXCEEDED
        - MIN_DOCUMENTS_REQUIRED
        - MULTI_DOCUMENT_READ_ONLY
        - MULTI_DOCUMENT_REPLACE_UNSUPPORTED
        - NOT_FOUND
        - PAGE_COUNT_MISMATCH
        - PARTICIPANTS_CHANGED
        - RATE_LIMIT_EXCEEDED
        - SEAL_MUTATION_NOT_ALLOWED
        - SEND_EFFECTS_EXIST
        - SEND_IN_PROGRESS
        - SIGNED_DATA_IMMUTABLE
        - SIGNING_ORDER_LOCKED_APPLIED
        - SIGNING_ORDER_LOCKED_SENDING
        - SIGNING_ORDER_LOCKED_SENT
        - VALIDATION_ERROR
      description: >-
        Error codes of the multi-document contract and of the routes it changes
        (generated from the error matrix's API-surfaced rows). A genuine server
        fault answers 500 with INTERNAL_ERROR on the legacy routes and INTERNAL
        on the shared HttpError paths (multi-document dispatch, the RPC status
        map, document-set routes); neither carries detail.
    SealSummary:
      type: object
      description: Summary of an organization seal.
      required:
        - id
        - lineage_id
        - name
        - version
        - display_name
      properties:
        id:
          type: string
          format: uuid
        lineage_id:
          type: string
          format: uuid
          description: Shared across versions of the same seal
        name:
          type: string
        version:
          type: integer
        display_name:
          type: string
  responses:
    UnauthorizedError:
      description: Unauthorized - Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Unauthorized
            message: Invalid API key
    NotFoundError:
      description: Not Found - Resource does not exist
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Not Found
            message: The requested resource was not found
    RateLimitError:
      description: Too Many Requests - Rate limit exceeded
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
          description: Maximum requests per minute
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: Requests remaining
        X-RateLimit-Reset:
          schema:
            type: integer
          description: Unix timestamp of reset
        Retry-After:
          schema:
            type: integer
          description: Seconds until retry allowed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Rate Limit Exceeded
            message: Too many requests. Please wait before retrying.
            details:
              retry_after: 45
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        API key for authentication. Use your API key directly without any prefix
        (e.g., 'your-api-key'). Bearer prefix is optional but not required.

````

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