Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: HonkIO Email Templates API
description: 'Canadian-focused SMS gateway API.
## Canadian Compliance
- **CASL**: All commercial messages require consent. Use `/v1/compliance/consents` to record.
- **DNCL**: CRTC Do Not Call List checking is coming soon (not yet enforced).
- **PIPEDA / Quebec Law 25**: Data is stored in Canada (ca-central-1). Requests are processed in the United States in transit. Right-to-erasure available.
## Authentication
Provide your API key via `Authorization: Bearer mk_live_...` or `X-API-Key: mk_live_...`.
Test keys never send, never charge and can''t change live data; they work before your first top-up.
## Rate limits
100 requests per second per account. Every authenticated response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.
## Pagination
Shape follows the route family, not one convention across the API: documented here rather than changed. Messages listings return `{ data, meta: { page, limit, total, pages } }`. Contacts, Contact Groups, Lists and Verify listings return `{ data, total, limit, offset }`. Compliance consent and opt-out listings accept `page`/`limit` and return `{ data }`, with no total. Account transactions use a cursor: pass `before`, get back `has_more`.'
version: 1.0.0
contact:
name: HonkIO Support
email: support@honkio.ca
servers:
- url: https://api.honkio.ca
description: Production
- url: http://localhost:3000
description: Local development
security:
- apiKey: []
- apiKeyHeader: []
tags:
- name: Email Templates
description: Store email templates and publish versions to send by id or alias
paths:
/v1/email-templates:
post:
summary: Create an email template
tags:
- Email Templates
description: 'Saves a draft. A live send uses a template once it is published; a send with a test key renders the draft, published or not, so a test key can build and try a template without publishing it.
Requires `email_templates:w`.'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 200
description: A label for you, up to 200 characters.
alias:
default: null
type:
- string
- 'null'
pattern: ^[a-z0-9][a-z0-9_-]{0,62}$
description: 'Send with it instead of the id: 1 to 63 lowercase letters, digits, - or _, starting with a letter or digit. Null removes it.'
subject:
type: string
minLength: 1
maxLength: 998
description: Up to 998 characters. Variables go in double braces, such as {{ first_name }}.
html:
type:
- string
- 'null'
description: The HTML body. A template needs html, text or both. Variable values are always HTML escaped.
example: <p>Hi {{ first_name }}, your order has shipped.</p>
text:
type:
- string
- 'null'
description: The plain text body.
from:
type:
- string
- 'null'
description: The default sender. A send can set its own.
reply_to:
anyOf:
- type: string
format: email
- type: array
items:
type: string
format: email
description: 'The default reply address: a string or an array. A send can set its own.'
variables:
default: []
type: array
items:
type: object
properties:
key:
type: string
minLength: 1
maxLength: 64
description: 'The variable''s name: up to 64 characters.'
fallback:
type: string
maxLength: 1000
description: 'Used when a send gives no value: up to 1000 characters.'
required:
- key
additionalProperties: false
description: Every variable the template uses, each optionally with a fallback. A send that leaves one with neither a value nor a fallback is refused.
required:
- name
- subject
additionalProperties: false
description: 'A new template. It saves as a draft: publish it before sending with it from a live key.'
description: 'A new template. It saves as a draft: publish it before sending with it from a live key.'
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:w
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: The API key lacks the permission this operation requires, or the account is suspended or closed.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: postV1EmailTemplates
x-operation-id-source: derived
get:
summary: List email templates
tags:
- Email Templates
description: Requires `email_templates:r`.
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:r
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: The API key lacks the permission this operation requires, or the account is suspended or closed.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: getV1EmailTemplates
x-operation-id-source: derived
/v1/email-templates/{idOrAlias}:
get:
summary: Get an email template by id or alias
tags:
- Email Templates
description: Requires `email_templates:r`.
parameters:
- schema:
type: string
minLength: 1
maxLength: 200
in: path
name: idOrAlias
required: true
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:r
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: The API key lacks the permission this operation requires, or the account is suspended or closed.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: getV1EmailTemplatesByIdOrAlias
x-operation-id-source: derived
patch:
summary: Edit an email template draft
tags:
- Email Templates
description: 'Changes the draft. Live sends keep the published version until you publish again; a send with a test key renders the draft. The alias is not part of the draft: a change to it applies to sends at once, so on a template with a published version a test key cannot change it (LIVE_KEY_REQUIRED).
Requires `email_templates:m`.'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 200
description: A label for you, up to 200 characters.
alias:
type:
- string
- 'null'
pattern: ^[a-z0-9][a-z0-9_-]{0,62}$
description: 'Send with it instead of the id: 1 to 63 lowercase letters, digits, - or _, starting with a letter or digit. Null removes it.'
subject:
type: string
minLength: 1
maxLength: 998
description: Up to 998 characters. Variables go in double braces, such as {{ first_name }}.
html:
type:
- string
- 'null'
description: The HTML body. A template needs html, text or both. Variable values are always HTML escaped.
text:
type:
- string
- 'null'
description: The plain text body.
from:
type:
- string
- 'null'
description: The default sender. A send can set its own.
reply_to:
anyOf:
- type: string
format: email
- type: array
items:
type: string
format: email
description: 'The default reply address: a string or an array. A send can set its own.'
variables:
type: array
items:
type: object
properties:
key:
type: string
minLength: 1
maxLength: 64
description: 'The variable''s name: up to 64 characters.'
fallback:
type: string
maxLength: 1000
description: 'Used when a send gives no value: up to 1000 characters.'
required:
- key
additionalProperties: false
description: Every variable the template uses, each optionally with a fallback. A send that leaves one with neither a value nor a fallback is refused.
additionalProperties: false
description: The fields to change in the draft. Live sends keep using the published version until you publish again.
description: The fields to change in the draft. Live sends keep using the published version until you publish again.
parameters:
- schema:
type: string
minLength: 1
maxLength: 200
in: path
name: idOrAlias
required: true
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:m
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: 'LIVE_KEY_REQUIRED: a test key cannot change the alias of a template that has a published version, because live sends may use the alias. It can edit every other field of the draft. Also FORBIDDEN when the key lacks the permission this operation requires, or the account is suspended or closed.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: patchV1EmailTemplatesByIdOrAlias
x-operation-id-source: derived
delete:
summary: Delete an email template and its versions
tags:
- Email Templates
description: 'Sends that name the template then answer NOT_FOUND. Refused with EMAIL_TEMPLATE_IN_USE while a scheduled email uses it. Live keys only (LIVE_KEY_REQUIRED).
Requires `email_templates:d`.'
parameters:
- schema:
type: string
minLength: 1
maxLength: 200
in: path
name: idOrAlias
required: true
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:d
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: 'LIVE_KEY_REQUIRED: templates have no test mode, and this changes what live sends get. A test key can create a template, edit its draft and send test emails that render the draft. Also FORBIDDEN when the key lacks the permission this operation requires, or the account is suspended or closed.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: deleteV1EmailTemplatesByIdOrAlias
x-operation-id-source: derived
/v1/email-templates/{idOrAlias}/publish:
post:
summary: Publish the draft as a new version
tags:
- Email Templates
description: 'Live sends use the new version at once. Live keys only (LIVE_KEY_REQUIRED): a send with a test key renders the draft and needs no publish.
Requires `email_templates:m`.'
parameters:
- schema:
type: string
minLength: 1
maxLength: 200
in: path
name: idOrAlias
required: true
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:m
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: 'LIVE_KEY_REQUIRED: templates have no test mode, and this changes what live sends get. A test key can create a template, edit its draft and send test emails that render the draft. Also FORBIDDEN when the key lacks the permission this operation requires, or the account is suspended or closed.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: postV1EmailTemplatesByIdOrAliasPublish
x-operation-id-source: derived
/v1/email-templates/{idOrAlias}/rollback:
post:
summary: Publish an earlier version again as a new version
tags:
- Email Templates
description: 'Replaces the draft with the earlier version and publishes it. Live keys only (LIVE_KEY_REQUIRED).
Requires `email_templates:m`.'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
version:
type: integer
minimum: 1
maximum: 9007199254740991
description: A published version number, from GET /v1/email-templates/{idOrAlias}/versions.
required:
- version
additionalProperties: false
description: The version to publish again.
description: The version to publish again.
parameters:
- schema:
type: string
minLength: 1
maxLength: 200
in: path
name: idOrAlias
required: true
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:m
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: 'LIVE_KEY_REQUIRED: templates have no test mode, and this changes what live sends get. A test key can create a template, edit its draft and send test emails that render the draft. Also FORBIDDEN when the key lacks the permission this operation requires, or the account is suspended or closed.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: postV1EmailTemplatesByIdOrAliasRollback
x-operation-id-source: derived
/v1/email-templates/{idOrAlias}/versions:
get:
summary: List published versions, newest first
tags:
- Email Templates
description: Requires `email_templates:r`.
parameters:
- schema:
type: string
minLength: 1
maxLength: 200
in: path
name: idOrAlias
required: true
security:
- apiKey: []
- apiKeyHeader: []
x-honkio-permission: email_templates:r
responses:
'401':
description: Missing, invalid, revoked or expired API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: The account has no balance, or a LIVE key was used before the first top-up.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: The API key lacks the permission this operation requires, or the account is suspended or closed.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Rate limit reached: 100 requests per second per account, or a route-specific limit (Retry-After set where one applies).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected server error.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
operationId: getV1EmailTemplatesByIdOrAliasVersions
x-operation-id-source: derived
components:
schemas:
Error:
type: object
description: Standard error body. The one visible exception is the OAuth 2.1 surface (/oauth/token, /oauth/register, /oauth/revoke), which follows RFC 6749/7591/7009 and answers { error, error_description } instead.
required:
- code
- message
- statusCode
properties:
code:
type: string
description: Machine-readable error code, e.g. VALIDATION_ERROR.
message:
type: string
description: Human-readable message in the request's locale (Accept-Language).
messageEn:
type: string
description: English message, always present alongside message.
messageFr:
type: string
description: French message, always present alongside message.
statusCode:
type: integer
description: The HTTP status, repeated in the body.
details:
description: 'Present on some errors: an AJV validation array, or error-specific structured detail.'
securitySchemes:
apiKey:
type: http
scheme: bearer
bearerFormat: API Key
description: Use `mk_live_...` for production or `mk_test_...` for testing.
apiKeyHeader:
type: apiKey
in: header
name: X-API-Key
description: 'Alternative to the Authorization header: send `mk_live_...` or `mk_test_...` as X-API-Key directly.'