---
title: "Check template"
method: POST
path: "/templates/{templateId}/check"
tags: ["Templates"]
---

# Check template

`POST /templates/{templateId}/check`

## Request

- Base URL: `https://api.sequenzy.com/api/v1`
- URL: `https://api.sequenzy.com/api/v1/templates/{templateId}/check`
- Auth: HTTP bearer

Run the pre-send email check on a saved template: the same rules as the editor's email checker, plus live verification of every link and image, including links in every A/B variant and translation unless variantId or locale pins one version. Read-only: this never sends or modifies anything, and uses POST only so personalization input can travel in a request body.

## Path parameters

- `templateId` string, required

## Request body

- CheckEmailRequest: Personalization and options for an email check. Omit every field to check for a sample contact with live link verification on.
  - `subscriberId` string: Check as this stored subscriber: picks their localization and reports merge tags and conditions that would not resolve for them. Link and content rules always run on the email as written. Mutually exclusive with subscriber. Requires the subscribers:read scope as well.
  - `subscriber` object: Check as an ad-hoc contact. Mutually exclusive with subscriberId.
    - `email` string, email, required
    - `firstName` string, nullable
    - `lastName` string, nullable
    - `customAttributes` object
    - `tags` string[]: Tags this ad-hoc contact carries, for tag block conditions.
  - `variables` object: Extra merge variables layered over the contact's attributes.
  - `locale` string: Check a specific localization instead of deriving it from the contact.
  - `variantId` string: Check a specific A/B test variant. Required for sequence steps whose nodeType is action_ab_test. Sequence variants also need the ab_tests:read scope. Ignored for templates.
  - `variationId` string: Check a design variation of the campaign's or step's email instead of its saved content. It must be ready; it can't be combined with variantId. Ignored for templates.
  - `links` boolean: Verify every link and image over the network: HTTP status, redirects, DNS, TLS certificates, and image type and size. Pass false for a fast, rules-only check.

## Response `200`

Check completed

- CheckEmailResponse
  - `success` boolean
  - `score` integer: Overall score across subject (25%), preview text (15%) and content (40%), including live link findings, rescaled to 100. Sender settings are not part of this check, so the editor's score (which weighs the sender at 20%) can differ slightly.
  - `grade` 'A' | 'B' | 'C' | 'D' | 'F'
  - `placement` 'Primary' | 'Promotions' | 'Spam': Predicted inbox tab.
  - `categories` object
    - `subject` EmailCheckCategoryScore
      - `score` integer
      - `maxScore` integer
    - `preview` EmailCheckCategoryScore
      - `score` integer
      - `maxScore` integer
    - `content` EmailCheckCategoryScore
      - `score` integer
      - `maxScore` integer
  - `issues` EmailCheckIssue[]: Findings sorted by severity, errors first.
    - `category` 'subject' | 'preview' | 'content', required: Score category the finding counts against.
    - `severity` 'error' | 'warning' | 'info', required: error - fix before sending. warning - worth reviewing. info - a tip that does not need action.
    - `message` string, required: Plain-language description of the finding.
    - `rule` string: Stable rule ID. The prefix groups it: links, images, content, subject, preview, accessibility, or compatibility. Examples: links.broken, links.unreachable, links.missing-scheme, links.local, links.staging, links.example-domain, links.insecure, links.shortener, links.text-mismatch, links.missing-website, images.broken, images.large, images.missing-alt, content.placeholder-text, content.merge-tag-fallback, subject.fake-reply, compatibility.gmail-clip.
    - `blockId` string: Block to edit, when the finding has one location.
    - `url` string: The URL concerned, for link and image findings.
  - `links` EmailCheckLink[]: Every link and image in the email with its result.
    - `url` string: The URL as it will be sent. {{company.url}} is replaced with the company website; other merge tags are left as authored.
    - `kind` 'link' | 'image'
    - `label` string: Button text, link text, or image alt text.
    - `blockId` string, nullable: Block that contains the URL.
    - `blockType` string, nullable
    - `status` 'ok' | 'broken' | 'invalid' | 'server_error' | 'unreachable' | 'restricted' | 'personalized' | 'not_checked': ok - answered with a success status (after redirects). broken - page or domain not found, certificate problem, redirect loop, or rejected request. invalid - can never work for recipients: a rule already found it unusable (localhost, a private address, a mistyped or missing https://), or the domain points to a private address. server_error - 5xx answer. unreachable - timed out or refused. restricted - the site refused an automated check (for example 401, 403 or 429); it may work in a browser and is not counted as broken. personalized - built from merge tags filled per recipient, so only its syntax is checked. not_checked - mailto and tel links, links not reached before the 25-second limit, or live checks turned off.
    - `httpStatus` integer, nullable: Final HTTP status code, when a response was received.
    - `finalUrl` string, nullable: Where the URL ended up after redirects, when it redirected.
    - `message` string: Plain-language result, such as "Page not found (404)".
    - `findings` object[]: No-network rule results for this URL.
      - `rule` string
      - `severity` 'error' | 'warning' | 'info'
      - `message` string
  - `linkSummary` object
    - `total` integer
    - `checked` integer
    - `ok` integer
    - `broken` integer: Links with status broken or invalid.
    - `warnings` integer: Links with status server_error or unreachable.
    - `restricted` integer
    - `notChecked` integer: Links with status personalized or not_checked.
  - `linksChecked` boolean: False when links was false in the request.
  - `subject` string: Subject line with merge tags resolved for the checked contact.
  - `previewText` string, nullable
  - `locale` string
  - `unresolvedMergeTags` object[]: Same as the render endpoints' unresolvedMergeTags.
    - `tag` string
    - `reason` 'unknown' | 'no_value'
  - `unevaluatedConditions` object[]: Same as the render endpoints' unevaluatedConditions.
  - `entity` object
    - `type` 'campaign' | 'sequence_step' | 'template'
    - `id` string
    - `variantId` string, nullable

## Other responses

- `400`: Validation error
- `401`: Unauthorized
- `403`: API key is missing the required read scope, or is missing subscribers:read while subscriberId was supplied
- `404`: Template not found
- `422`: Request body failed validation
- `429`: Too many live link checks (30 a minute per API key and company, 60 a minute per API key across companies). Retry later, or pass links: false for a rules-only check.

## Changes

- **2026-10-16** `db3873ca3d4d`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/sequenzy/apis/sequenzy-api/changes/templates/:templateId/check/post.md)

---

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