> ## 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.

# Create an organization seal

> Create a new organization seal with an uploaded, typed, or drawn image. Company-scope seals require a protected API key.



## OpenAPI

````yaml api-reference/v01.39.01/openapi-v01.39.01.json post /seals
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, enablement required)**: 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 only for companies enabled for
    multi-document documents; for any other company the routes answer `404
    NOT_FOUND` and the keys are absent.


    **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 differs for companies
    enabled for multi-document documents. `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:
  /seals:
    post:
      tags:
        - Organization Seals
      summary: Create an organization seal
      description: >-
        Create a new organization seal with an uploaded, typed, or drawn image.
        Company-scope seals require a protected API key.
      operationId: createOrganizationSeal
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrganizationSealCreate'
      responses:
        '201':
          description: Seal created successfully
          headers:
            X-Firma-Deprecation:
              schema:
                type: string
              description: >-
                Present when the operation is available but will be moved to the
                primary API host in a future release. The value describes the
                timeline.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationSeal'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '409':
          description: >-
            Conflict. Possible codes: SEAL_ALREADY_REVOKED,
            SEAL_ORDER_COLLISION, SEAL_ERASE_NOT_ELIGIBLE
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    $ref: '#/components/schemas/SealErrorCode'
        '429':
          $ref: '#/components/responses/RateLimitError'
        '503':
          description: >-
            Seal image processing is unavailable on this host
            (SEAL_CREATION_DISABLED_EDGE). Retry against the primary API host.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - ApiKeyAuth: []
components:
  schemas:
    OrganizationSealCreate:
      type: object
      required:
        - name
        - display_name
        - kind
        - scope
        - statement
      description: Request body to create an organization seal.
      properties:
        name:
          type: string
          maxLength: 120
          description: Internal name for the seal
        display_name:
          type: string
          maxLength: 200
          description: Display name rendered on the seal image
        signatory_title:
          type: string
          maxLength: 200
          nullable: true
          description: Signatory title rendered on the seal image
        kind:
          type: string
          enum:
            - uploaded
            - typed
            - drawn
          description: How the seal image was created
        scope:
          type: string
          enum:
            - company
            - workspace
          description: Scope of the seal. Company-scope requires a protected API key.
        workspace_id:
          type: string
          format: uuid
          description: Required when scope is 'workspace'
        image:
          type: string
          description: >-
            Base64-encoded PNG data URI (data:image/png;base64,...). Required
            for 'uploaded' and 'drawn' kinds.
        typed:
          type: object
          description: Typed seal parameters. Required for 'typed' kind.
          properties:
            text:
              type: string
              description: Text to render on the seal
            style:
              type: string
              description: Style variant for the typed seal
        statement:
          $ref: '#/components/schemas/SealStatement'
        is_default:
          type: boolean
          default: false
          description: Set as default seal for this scope
    OrganizationSeal:
      type: object
      description: >-
        Organization seal metadata (image and statement internals are stripped
        from list/get responses).
      properties:
        id:
          type: string
          format: uuid
        lineage_id:
          type: string
          format: uuid
          description: Shared across versions of the same seal
        version:
          type: integer
          minimum: 1
        companies_id:
          type: string
          format: uuid
        companies_workspaces_id:
          type: string
          format: uuid
          nullable: true
          description: Null for company-scope seals
        authority_name:
          type: string
          nullable: true
          description: >-
            The name the seal was attested for: the company for company scope or
            the protected workspace, otherwise the workspace. Null for seals
            whose lineage was created before pinning; they certify in the
            company's current name.
        name:
          type: string
          maxLength: 120
        display_name:
          type: string
          maxLength: 200
        signatory_title:
          type: string
          maxLength: 200
          nullable: true
        kind:
          type: string
          enum:
            - uploaded
            - typed
            - drawn
        is_default:
          type: integer
          enum:
            - 0
            - 1
          description: 1 if this is the default seal for its scope
        statement_language:
          type: string
        statement_version:
          type: integer
          minimum: 1
        signatory_name:
          type: string
          maxLength: 200
        signatory_title_attested:
          type: string
          maxLength: 200
          nullable: true
        attested_at:
          type: string
          format: date-time
        revoked_on:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        deleted:
          type: integer
          enum:
            - 0
            - 1
    SealErrorCode:
      type: string
      enum:
        - 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
      description: Error codes specific to organization seal operations.
    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
    SealStatement:
      type: object
      required:
        - language
        - signatory_name
        - text
        - accepted
      description: Attestation statement affirming authority to apply the seal.
      properties:
        language:
          type: string
          description: Language code of the statement
        signatory_name:
          type: string
          maxLength: 200
          description: Name of the person attesting
        signatory_title:
          type: string
          maxLength: 200
          nullable: true
          description: Title of the person attesting
        text:
          type: string
          description: >-
            On create, the statement rendered with the authority name (the
            stored workspace or company name exactly as returned by the API,
            including HTML escaping). On replace, it must equal the seal's
            stored statement text.
        accepted:
          type: boolean
          description: Must be true to confirm acceptance
        version:
          type: integer
          minimum: 1
          description: Statement version number
  responses:
    ValidationError:
      description: Bad Request - Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Validation Error
            message: Invalid input data
            details:
              name: Name is required
              email: Invalid email format
    UnauthorizedError:
      description: Unauthorized - Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Unauthorized
            message: Invalid API key
    ForbiddenError:
      description: Forbidden - Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Forbidden
            message: You do not have permission to access this resource
    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.