---
title: "Create a template from detection"
method: POST
path: "/templates/from-detection"
tags: ["Templates"]
---

# Create a template from detection

`POST /templates/from-detection`

## Request

- Base URL: `https://inkseal.net/api/v1`
- URL: `https://inkseal.net/api/v1/templates/from-detection`
- Auth: HTTP bearer

Creates roles and fields from one completed detection run. When runId is omitted, the latest completed run is selected by completed time and id. Honors Idempotency-Key and replays the exact original template and review response.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `documentId` string, required
  - `runId` integer
  - `name` string, required
  - `description` string, nullable

## Response `201`

The created template and the exact fields requiring review.

- object
  - `data` object, required
    - `template` TemplateDetail, required
      - `id` string, required: Public template id. This is the `templateId` in the `/templates/{templateId}` routes and the `templateId` a template envelope is created from.
      - `name` string, required
      - `description` string, nullable, required
      - `document` object, required
        - `id` string, required
        - `filename` string, required
        - `pageCount` integer, required
        - `pageDimensions` object[], required
          - `width` number, required
          - `height` number, required
      - `signingOrder` 'parallel' | 'sequential', required
      - `defaultSubject` string, nullable, required
      - `defaultMessage` string, nullable, required
      - `defaultExpiresInDays` integer, required
      - `emailVerification` boolean, required
      - `version` integer, required
      - `complete` boolean, required
      - `archivedAt` string, date-time, nullable, required
      - `createdAt` string, date-time, required
      - `updatedAt` string, date-time, required
      - `roles` TemplateRole[], required
        - `key` string, required
        - `label` string, required
        - `order` integer, required
        - `id` string, required
      - `fields` TemplateField[], required
        - `name` string, required
        - `type` 'signature' | 'initials' | 'text' | 'date' | 'checkbox' | 'number' | 'email' | 'select' | 'radio' | 'textarea', required: How the field is filled and rendered. Values are stored as strings; signature and initials use images.
        - `assignee` union, required
          - 'sender'
          - object
            - `role` string, required
        - `pageIndex` integer, required
        - `xRatio` number, required
        - `yRatio` number, required
        - `widthRatio` number, required
        - `heightRatio` number, required
        - `required` boolean, required
        - `readonly` boolean, required
        - `defaultValue` string, nullable, required
        - `validation` FieldValidation, required
          - `minLength` integer
          - `maxLength` integer
          - `pattern` string
          - `min` number
          - `max` number
          - `minDate` string, date
          - `maxDate` string, date
        - `options` FieldOption[], nullable, required
          - `value` string, required
          - `label` string, required
        - `label` string, nullable, required
        - `id` string, required
    - `review` object, required
      - `lowConfidenceFields` string[], required
      - `unassignedFields` string[], required

## Other responses

- `400`: The request is malformed or the resource is in the wrong state for a synchronous check. Codes: `validation_error`, `invalid_body`, `invalid_parameter`, `invalid_idempotency_key`, `invalid_event_id`, `invalid_file`, `invalid_url`, `invalid_status`, `key_mismatch`, `no_signers`, `not_completed`, `envelope_exists`, `document_not_ready`, `upload_failed`.
- `401`: The credential is missing, malformed, unknown, expired, revoked, or past its rotation grace window. Code: `unauthorized`. Every authentication failure is deliberately indistinguishable.
- `403`: The credential is valid but may not do this. Codes: `insufficient_scope` (the route's scopes are not all held; `details` names `requiredScopes` and `missingScopes`), `forbidden` (the capability check on the resolved resource failed), `scope_escalation` (a grant wider than the caller's own was requested).
- `404`: The resource does not exist, was deleted, or belongs to another workspace. Code: `not_found`. The three cases are answered identically on purpose, so a 404 never confirms that an identifier exists elsewhere.
- `409`: The resource's state forbids this transition, or another request owns the work. Codes: `invalid_state`, `invalid_status`, `credential_state`, `limit_exceeded`, `envelope_missing`, `not_retryable`, `owner_unavailable`, `workspace_unavailable`, `detection_not_completed`, `document_in_use`, `version_conflict`, `document_processing`, `document_processing_failed`, `normalized_pdf_unavailable`, `pages_not_rendered`, `document_not_ready`, `idempotency_in_progress` (which also carries `Retry-After`), `payment_replayed` (a USDC payment signature presented twice), `payment_pending` (a USDC settlement whose outcome is not yet known).
- `422`: The request parsed but its content is not acceptable. Codes: `validation_error` (envelope configuration, with an `issues` array in `details`), `invalid_envelope`, `envelope_not_ready`, `document_page_limit`, `idempotency_key_reuse`.
- `429`: A limit was reached. Codes: `rate_limited` (the credential's per-bucket HTTP limit, or the per-workspace detection throttle), `workspace_limit_exceeded` (a workspace content or send limit; `details.limit` names it), `daily_budget_exceeded` (the workspace's daily detection budget). The four rate-limit headers are sent on this response and on no successful one, so a client cannot read its remaining budget without being refused. Retrying after `Retry-After` is always safe: a rate-limit refusal happens before the handler runs.
- `503`: A dependency the request needs is unreachable and nothing was changed. Codes: `queue_unavailable` (a background job could not be enqueued; retry the call), `billing_unavailable` (Stripe would not open a checkout session; nothing was charged, retry the call), `service_unavailable` (the rate-limit store is down, so the request was refused before running).

---

[API](https://skmtc.dev/inkseal/apis/inkseal-api.md) · [All operations](https://skmtc.dev/inkseal/apis/inkseal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/inkseal/apis/inkseal-api/revisions/c440c981d578?raw)
