openapi: 3.0.0
info:
contact: {}
description: API for managing email content, templates and universal layouts
title: Email Universal Layouts API
version: '5.0'
x-api-evangelist:
harvested: '2026-08-13'
method: searched
source: https://dash.readme.com/api/v1/api-registry/9c1ckrq8msfxryye
source-note: Published by Omnisend on its own docs host api-docs.omnisend.com (ReadMe project @omnisend,
branch v2026-03-15); registry document referenced by the reference page as oasPublicUrl.
paths:
/email-universal-layouts:
get:
description: 'With this endpoint you can get a paginated list of universal layouts.
**Sorting:**
Items are returned in descending order by creation time (newest first).
This is based on the internal document ID which contains a timestamp.
**Pagination:**
This endpoint uses cursor-based pagination for efficient traversal of large datasets.
- Use the `paging.cursors.after` value from the response to get the next page
- Use the `paging.cursors.before` value from the response to get the previous page
- The `paging.hasMore` field indicates if more results are available
- Do not use both `after` and `before` parameters simultaneously
- Maximum page size is 250 items (default 100)
- If `limit` is less than 1 or greater than 250, returns 400 Bad Request
**Filtering:**
Use `nameContains` to filter layouts by name (case-insensitive partial match, max 200 characters)
**Scopes:**
`email-templates.read`
**Rate Limiting:**
This endpoint is rate limited to 400 requests per minute.'
parameters:
- description: Number of items per page (1-250, default 100)
in: query
name: limit
schema:
type: integer
minimum: 1
maximum: 250
- description: Cursor for next page (base64-encoded, from previous response)
in: query
name: after
schema:
type: string
- description: Cursor for previous page (base64-encoded, from previous response)
in: query
name: before
schema:
type: string
- description: Filter layouts by name (case-insensitive partial match, max 200 characters)
in: query
name: nameContains
schema:
type: string
maxLength: 200
- $ref: '#/components/parameters/APIVersionHeader'
responses:
'200':
description: List of universal layouts with pagination
content:
application/json:
schema:
$ref: '#/components/schemas/ListUniversalLayoutsResponse'
'400':
description: Invalid query parameters or missing brand ID
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'401':
description: Authentication is missing or invalid
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'403':
description: Insufficient permissions for this operation
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'410':
description: API version has been retired
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'500':
description: Unexpected error occurred
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
security:
- Bearer:
- email-templates.read
- ApiKeyAuth: []
summary: Get universal layouts
tags:
- Email Universal Layouts
post:
description: 'With this endpoint you can create a new universal layout.
**Scopes:**
`email-templates.write`
**Rate Limiting:**
This endpoint is rate limited to 400 requests per minute.'
parameters:
- $ref: '#/components/parameters/APIVersionHeader'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UniversalLayout'
description: Universal layout to create
required: true
responses:
'201':
description: Created Universal Layout
content:
application/json:
schema:
$ref: '#/components/schemas/UniversalLayout'
'400':
description: Invalid request body, missing brand ID, invalid layout structure, or validation
errors
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'401':
description: Authentication is missing or invalid
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'403':
description: Insufficient permissions for this operation
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'410':
description: API version has been retired
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'413':
description: Request body too large - exceeds 1 MB limit
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'500':
description: Unexpected error occurred
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
security:
- Bearer:
- email-templates.write
- ApiKeyAuth: []
summary: Create universal layout
tags:
- Email Universal Layouts
/email-universal-layouts/{id}:
delete:
description: 'With this endpoint you can delete a universal layout by ID.
**Scopes:**
`email-templates.write`
**Rate Limiting:**
This endpoint is rate limited to 400 requests per minute.'
parameters:
- description: Universal Layout ID (24 character hexadecimal)
in: path
name: id
required: true
schema:
type: string
- $ref: '#/components/parameters/APIVersionHeader'
responses:
'204':
description: Universal layout deleted successfully
'400':
description: Invalid layout ID or missing brand ID
content:
'*/*':
schema:
$ref: '#/components/schemas/APIErrorResponse'
'401':
description: Authentication is missing or invalid
content:
'*/*':
schema:
$ref: '#/components/schemas/APIErrorResponse'
'403':
description: Insufficient permissions for this operation
content:
'*/*':
schema:
$ref: '#/components/schemas/APIErrorResponse'
'410':
description: API version has been retired
content:
'*/*':
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded
content:
'*/*':
schema:
$ref: '#/components/schemas/APIErrorResponse'
'500':
description: Unexpected error occurred
content:
'*/*':
schema:
$ref: '#/components/schemas/APIErrorResponse'
security:
- Bearer:
- email-templates.write
- ApiKeyAuth: []
summary: Delete universal layout
tags:
- Email Universal Layouts
get:
description: 'With this endpoint you can get a universal layout by ID.
**Scopes:**
`email-templates.read`
**Rate Limiting:**
This endpoint is rate limited to 400 requests per minute.'
parameters:
- description: Universal Layout ID (24 character hexadecimal)
in: path
name: id
required: true
schema:
type: string
- $ref: '#/components/parameters/APIVersionHeader'
responses:
'200':
description: Universal Layout
content:
application/json:
schema:
$ref: '#/components/schemas/UniversalLayout'
'400':
description: Invalid layout ID or missing brand ID
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'401':
description: Authentication is missing or invalid
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'403':
description: Insufficient permissions for this operation
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Universal layout not found
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'410':
description: API version has been retired
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'500':
description: Unexpected error occurred
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
security:
- Bearer:
- email-templates.read
- ApiKeyAuth: []
summary: Get universal layout
tags:
- Email Universal Layouts
put:
description: 'With this endpoint you can update (fully replace) a universal layout by ID.
**Scopes:**
`email-templates.write`
**Rate Limiting:**
This endpoint is rate limited to 400 requests per minute.'
parameters:
- description: Universal Layout ID (24 character hexadecimal)
in: path
name: id
required: true
schema:
type: string
- $ref: '#/components/parameters/APIVersionHeader'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UniversalLayout'
description: Universal layout to update
required: true
responses:
'200':
description: Updated Universal Layout
content:
application/json:
schema:
$ref: '#/components/schemas/UniversalLayout'
'400':
description: Invalid layout ID, request body, missing brand ID, invalid layout structure, or
validation errors
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'401':
description: Authentication is missing or invalid
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'403':
description: Insufficient permissions for this operation
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'404':
description: Universal layout not found
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'410':
description: API version has been retired
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'413':
description: Request body too large - exceeds 1 MB limit
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'429':
description: Rate limit exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
'500':
description: Unexpected error occurred
content:
application/json:
schema:
$ref: '#/components/schemas/APIErrorResponse'
security:
- Bearer:
- email-templates.write
- ApiKeyAuth: []
summary: Update universal layout
tags:
- Email Universal Layouts
servers:
- url: https://api.omnisend.com/api
components:
parameters:
APIVersionHeader:
description: API version that specifies the response format and behaviour
in: header
name: Omnisend-Version
required: true
schema:
type: string
default: '2026-03-15'
securitySchemes:
ApiKeyAuth:
in: header
name: Authorization
type: apiKey
Bearer:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://app.omnisend.com/oauth2/token
scopes:
email-templates.read: Allows reading email templates and universal layouts
email-templates.write: Allows create, update, delete email templates and universal layouts
schemas:
APIErrorResponse:
description: RFC 9457 Problem Details error response
properties:
detail:
description: Human-readable explanation specific to this occurrence
example: One or more fields are invalid.
type: string
errors:
description: List of field-level validation errors
items:
$ref: '#/components/schemas/FieldError'
type: array
instance:
description: URI reference identifying the specific occurrence
example: urn:omnisend:request:abc123
type: string
status:
description: HTTP status code
example: 400
type: integer
title:
description: Short human-readable summary of the problem
example: Validation failed
type: string
type:
description: URI reference identifying the problem type
example: https://problems.omnisend.com/validation-failed
type: string
type: object
Annotation:
description: Gmail annotation settings
properties:
discountDescription:
description: Gmail discount description - description of the discount or promotion to display
in Gmail promotion tab together with the discount code.
example: 15% off
type: string
isEnabled:
description: Gmail annotation enabled
example: true
type: boolean
type: object
Block:
description: Block is a single element in the email template
properties:
button:
$ref: '#/components/schemas/Button'
components:
description: 'Components - the parts a block renders that are not covered by its own content
field.
Some blocks tag each part with a ''role'', and which roles they need is decided by their ''type'':
a ''discount'' block holds the code, the redeem button and the expiry date, a ''menu'' block
holds
its items, a ''product'' block holds the product''s image, title, description, prices and
button.
Other blocks nest components without a role, and blocks that render everything from a dedicated
content field have no components at all. See BlockComponent.'
items:
$ref: '#/components/schemas/BlockComponent'
type: array
discount:
$ref: '#/components/schemas/Discount'
dynamicDiscount:
$ref: '#/components/schemas/Discount'
html:
description: 'Deprecated: use HTMLCode instead. Read-only, will be removed in future versions.'
example: <p>Hello</p>
readOnly: true
type: string
htmlCode:
$ref: '#/components/schemas/HTMLCode'
id:
description: Block unique identifier - must be unique within the template
example: 6699a0000000000000000000
maxLength: 24
minLength: 24
type: string
image:
$ref: '#/components/schemas/Image'
lineSpace:
$ref: '#/components/schemas/LineSpace'
logo:
$ref: '#/components/schemas/Logo'
orderAddresses:
$ref: '#/components/schemas/OrderAddresses'
orderProducts:
$ref: '#/components/schemas/OrderProducts'
orderSummary:
$ref: '#/components/schemas/OrderSummary'
orderTotal:
$ref: '#/components/schemas/OrderTotal'
preheader:
$ref: '#/components/schemas/Preheader'
product:
$ref: '#/components/schemas/Product'
role:
description: Block role
enum:
- product_image
- product_title
- product_description
- product_prices
- product_price
- product_button
- discount_code
- discount_button
- discount_expiration_date
- product_current_price
- product_old_price
- menu_text
example: product_title
type: string
secondaryStylePresetID:
description: Secondary style preset ID - ID of the secondary style preset. Used for order blocks
styling as smaller text style.
example: paragraph
type: string
social:
$ref: '#/components/schemas/Social'
staticDiscount:
$ref: '#/components/schemas/Discount'
stylePresetID:
description: 'Style preset ID - ID of the style preset. Used for Button or Text block styling.
ID must be present in template General Settings.'
example: paragraph
type: string
styleProperties:
allOf:
- $ref: '#/components/schemas/StyleProperties'
description: Style properties - style properties of the block
text:
description: Text content of the block - supports HTML formatting and personalization tags
example: <p>Hello [[contact.first_name]]</p>
type: string
type:
description: "Block type.\nNotes:\n - 'html' is read-only and cannot be set on create/update.\n\
\ - 'menu' is a container block with no dedicated content field — items must be provided\
\ via 'components' as text blocks with role 'menu_text', and styling is configured through\
\ 'stylePresetID' and 'styleProperties'.\n - 'discount' is the block for an offer. The offer\
\ is configured in the 'discount' object, and everything the recipient sees must be provided\
\ via 'components' as role-tagged blocks — a text block with role 'discount_code' (required,\
\ exactly one), plus an optional button with role 'discount_button' and an optional text block\
\ with role 'discount_expiration_date'. The store issues a code per recipient when the email\
\ is sent, so 'discount.code' carries the placeholder XXXX-XXXX-XXXX. Writing the offer as\
\ plain text instead leaves a code that was never created in the store, so the recipient cannot\
\ redeem it.\n - 'dynamicDiscount' is the WooCommerce equivalent of 'discount' — same object,\
\ same components and placeholder, except that the 'discount_button' component is required.\
\ A 'discount' block on a WooCommerce store is skipped when the email is sent, so it ships\
\ the placeholder to the recipient as the code.\n - 'staticDiscount' (WooCommerce) instead\
\ carries one fixed code that already exists in the store, and gets no code at send time.\
\ It does not accept a 'discount_button'. Use it only when the brand explicitly asked for\
\ a fixed code."
enum:
- html
- htmlCode
- text
- image
- video
- logo
- menu
- social
- button
- product
- discount
- staticDiscount
- dynamicDiscount
- orderSummary
- orderProducts
- orderTotal
- orderAddresses
- lineSpace
- preheader
- price
example: text
type: string
video:
$ref: '#/components/schemas/Video'
type: object
BlockComponent:
description: 'BlockComponent is a block nested inside a parent block, carrying one part of what
that parent renders. Some parent blocks assign a ''role'' to each of their parts, and then the
parent''s ''type'' decides which roles it needs: a ''discount'' block requires text/discount_code
and accepts button/discount_button and text/discount_expiration_date; its WooCommerce equivalents
take the same roles, except that ''dynamicDiscount'' requires the button and ''staticDiscount''
does not accept one. A ''menu'' block holds its items as text/menu_text. The product_* roles belong
to a ''product'' block, whose price/product_prices component nests text/product_current_price
and text/product_old_price components of its own. Blocks that do not assign roles nest their components
without one.'
properties:
button:
allOf:
- $ref: '#/components/schemas/Button'
description: Button content of the component - used by roles 'discount_button' and 'product_button'.
components:
description: Components nested inside this component. Only role 'product_prices' uses them.
items:
$ref: '#/components/schemas/BlockComponent'
type: array
html:
description: 'Deprecated: use Text instead. Read-only, will be removed in future versions.'
example: <p>Hello</p>
readOnly: true
type: string
id:
description: Component unique identifier - must be unique within the template
example: 6699a0000000000000000000
maxLength: 24
minLength: 24
type: string
image:
allOf:
- $ref: '#/components/schemas/Image'
description: Image content of the component - used by role 'product_image'.
role:
description: 'Role the component plays inside its parent block - set only where the parent block
assigns
roles to its parts, and omitted otherwise.'
enum:
- discount_code
- discount_button
- discount_expiration_date
- menu_text
- product_image
- product_title
- product_description
- product_prices
- product_price
- product_button
- product_current_price
- product_old_price
example: discount_code
type: string
stylePresetID:
description: Style preset ID - ID of the style preset. ID must be present in template General
Settings.
example: paragraph
type: string
styleProperties:
allOf:
- $ref: '#/components/schemas/StyleProperties'
description: Style properties - style properties of the component
text:
description: 'Text content of the component - supports HTML formatting and personalization tags.
For role ''discount_code'' this is the code the recipient sees; on a platform that issues
its own codes
it is replaced at send time, so it carries the placeholder XXXX-XXXX-XXXX.'
example: <p>XXXX-XXXX-XXXX</p>
type: string
type:
description: Component type. Only these block types can be nested as a component.
enum:
- text
- button
- image
- price
example: text
type: string
type: object
Body:
description: Body settings for the template
properties:
backgroundColor:
description: Body background color
example: '#EDEEF0'
type: string
backgroundImageID:
description: Body background image ID - must be present in Image API
example: 68f0ad8a72105230579523cd
maxLength: 24
minLength: 24
type: string
backgroundRepeat:
description: Body background repeat
example: no-repeat
type: string
backgroundSize:
description: Body background size
example: cover
type: string
type: object
Button:
description: Button block settings
properties:
isFullWidth:
description: Button is full width
example: true
type: boolean
link:
description: Button link
example: https://www.omnisend.com
type: string
text:
description: Button text
example: Shop now
type: string
translationKey:
description: Button translation key - used for internationalization support
example: shop_now
type: string
type: object
ButtonPreset:
description: Button preset for consistent design
properties:
id:
description: Button preset unique identifier
example: primary_button
type: string
name:
description: Button preset name
example: Primary button
type: string
styles:
allOf:
- $ref: '#/components/schemas/ButtonStyle'
description: Button preset styles
type: object
ButtonStyle:
description: Button preset style properties
properties:
backgroundColor:
description: Button background color
example: '#383838'
type: string
border:
description: Button border style
example: '2px solid #BFDCFE'
type: string
borderRadius:
description: Button border radius
example: 0px
type: string
color:
description: Button font color
example: '#FFFFFF'
type: string
fontFamily:
description: Button font family
example: Alegreya, Georgia, Times New Roman, serif
type: string
fontSize:
description: Button font size
example: 16px
type: string
fontStyle:
description: Button font style
example: normal
type: string
fontWeight:
description: Button font weight
example: bold
type: string
letterSpacing:
description: Button letter spacing - spacing between letters
example: 0px
type: string
paddingBottom:
description: Button padding bottom
example: 16px
type: string
paddingLeft:
description: Button padding left
example: 16px
type: string
paddingRight:
description: Button padding right
example: 16px
type: string
paddingTop:
description: Button padding top
example: 16px
type: string
textDecoration:
description: Button text decoration
example: none
type: string
type: object
Column:
description: Column is a single column of the row
properties:
blocks:
description: Column blocks - used to group content into logical blocks
items:
$ref: '#/components/schemas/Block'
type: array
id:
description: Column unique identifier - must be unique within the template
example: 69770b2f7f774c6fb6f57194
maxLength: 24
minLength: 24
type: string
styleProperties:
allOf:
- $ref: '#/components/schemas/StyleProperties'
description: Column style properties - used to configure the column styling
width:
description: Column width - used to set the width of the column
example: 552px
type: string
type: object
Content:
description: Email content
properties:
createdAt:
description: Content created at (read-only)
example: '2021-01-01T01:02:03Z'
readOnly: true
type: string
generalSettings:
allOf:
- $ref: '#/components/schemas/GeneralSettings'
description: Content general settings - global and template level styles
id:
description: Content unique identifier (read-only)
example: 68beca12d2e99b0c8d19fd5e
readOnly: true
type: string
sections:
description: Content sections
items:
$ref: '#/components/schemas/Section'
type: array
updatedAt:
description: Content updated at (read-only)
example: '2021-01-01T01:02:03Z'
readOnly: true
type: string
type: object
CursorsResponse:
description: Cursor pointers for paginating forward and backward through results
properties:
after:
description: Opaque cursor for fetching the next page of results
example: eyJpZCI6ImNhbXAtNDU2In0
type: string
nullable: true
before:
description: Opaque cursor for fetching the previous page of results
example: eyJpZCI6ImNhbXAtMTIzIn0
type: string
nullable: true
type: object
CustomFont:
description: Custom font reference
properties:
id:
description: ID of the custom font
example: 69ca539b38e3c1f5dc035867
maxLength: 24
minLength: 24
type: string
type: object
Discount:
description: Discount block settings. This object configures the offer only — the parts the recipient
sees (code, redeem button, expiry) are separate role-tagged blocks in the parent block's 'components'.
See the notes on the Block 'type' field for the roles each discount block type requires. An offer
needs 'discountType', the value that matches it ('valuePercentage' for a percentage, 'valueFixed'
for an amount off), 'discountConditions', and 'endsIn' for how long it stays valid. 'staticDiscount'
and 'dynamicDiscount' blocks require 'code', 'discountType', 'valuePercentage' and 'discountConditions'.
properties:
appliesToCustomCollectionID:
description: ID of the custom collection that the discount applies to
example: 0
type: integer
appliesToID:
description: ID of the specific resource (product, collection) that the discount applies to
example: 0
type: integer
appliesToResource:
description: Type of resource the discount applies to
example: all
type: string
appliesToSmartCollectionID:
description: ID of the smart collection that the discount applies to
example: 0
type: integer
code:
description: 'Discount code customers use to apply the discount.
In ''discount'' (Shopify, BigCommerce) and ''dynamicDiscount'' (WooCommerce) blocks the code
is not taken from here:
t
# --- truncated at 32 KB (78 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/omnisend/refs/heads/main/openapi/omnisend-emailuniversallayouts-api-openapi.yml