In this documentation, all objects, request params, and responses are written as Typescript interfaces in order to make type declarations concise.
The Console API allows you to automate your newsletter-related tasks over HTTP with API key authentication. This is the same API that we internally use at the Console.
https://post.hyvor.com/api/consoleAuthorization header to Bearer <API_KEY>.GET - Retrieve a resourcePOST - Create a resource or perform an actionPUT - Update a resourceDELETE - Remove a resourceJSON (recommended) or as application/x-www-form-urlencoded.In this documentation, all objects, request params, and responses are written as Typescript interfaces in order to make type declarations concise.
The Console API endpoints are categorized based on the resource they interact with.
Jump to each category:
Endpoints:
GET /newsletter - Get newsletter dataPATCH /newsletter - Update a newsletterObjects:
GET /newsletter type Request = {}
type Response = NewsletterPATCH /newsletter type Request = Partial<Newsletter> // except id, created_at
type Response = NewsletterEndpoints:
GET /issues - Get issuesPOST /issues - Create an issueGET /issues/{id} - Get an issuePATCH /issues/{id} - Update an issueDELETE /issues/{id} - Delete an issuePOST /issues/{id}/send - Send an issueGET /issues/{id}/sends - Get issue sendsObjects:
GET /issues type Request = {
limit?: number; // default: 50
offset?: number; // default: 0
}
type Response = Issue[]POST /issues type Request = {}
type Response = IssueGET /issues/{id} type Request = {}
type Response = IssuePATCH /issues/{id} type Request = {
subject?: string;
lists?: number[];
content?: string;
sending_profile_id?: number;
}
type Response = IssueDELETE /issues/{id} type Request = {}
type Response = {}POST /issues/{id}/send type Request = {}
type Response = IssueGET /issues/{id}/sends type Request = {}
type Response = Send[]Endpoints:
POST /lists - Create a listPATCH /lists/{id} - Update a listDELETE /lists/{id} - Delete a listObjects:
POST /lists type Request = {
name: string; // max length: 255
description?: string;
}
type Response = ListPATCH /lists/{id} type Request = {
name?: string; // max length: 255
description?: string;
}
type Response = ListDELETE /lists/{id} type Request = {}
type Response = {}Endpoints:
GET /subscribers - Get subscribersGET /subscribers/email/{email} - Get a subscriber
by emailPOST /subscribers - Create or update a subscriberDELETE /subscribers/{id} - Delete a subscriberPOST /subscribers/bulk - Bulk update subscribersObjects:
GET /subscribers type Request = {
limit?: number; // default: 50
offset?: number; // default: 0
// filter by status
status?: 'subscribed' | 'unsubscribed' | 'pending';
// filter by list
list_id?: number;
// search by email
search?: string;
}
type Response = Subscriber[]GET /subscribers/email/{email} type Request = {}
type Response = Subscriber // 404 if not foundPOST /subscribers type Request = {
// If a subscriber with the given email already exists, it will be updated.
// Otherwise, a new subscriber will be created.
email: string;
// Subscribe to or unsubscribe from lists based
// on the given `lists_strategy`.
// an array of list IDs or names.
lists?: (number | string)[];
// The subscriber's subscription status
// default: subscribed
status?: 'subscribed' | 'pending';
// the source of the subscriber
// default: console
source?: 'console' | 'form' | 'import';
// subscriber's IP address
subscribe_ip?: string | null;
// unix timestamp of when the subscriber opted in
// if not set, it will be set to the current time if status is 'subscribed'
subscribed_at?: number | null; // unix timestamp
// additional metadata for the subscriber
// keys must be defined in the Subscriber Metadata Definitions section (or using the API)
metadata?: Record<string, string>;
// ============ SETTINGS ===========
// change how the endpoint behaves
// how `lists` field is processed when updating an existing subscriber's list subscriptions.
// merge: merges the lists (default)
// overwrite: overwrites the lists
// remove: removes from the current lists
lists_strategy?: 'merge' | 'overwrite' | 'remove';
// if the subscriber was previously removed from a list,
// define the reason(s) for ignoring the re-subscription to that list.
// see below for more info
// default: ['unsubscribe', 'bounce', 'complaint']
list_skip_resubscribe_on?: ('unsubscribe' | 'bounce' | 'complaint' | 'auto')[];
// define the reason for removing the subscriber from a list
// (only when updating, see below for more info)
// default: 'unsubscribe'
list_removal_reason?: 'unsubscribe' | 'bounce' | 'other';
// whether to overwrite or merge the subscriber's metadata
// when updating an existing subscriber.
// default: 'merge'
metadata_strategy?: 'merge' | 'overwrite';
// whether to send a confirmation email when adding a subscriber with 'pending' status
// or when changing an existing subscriber's status to 'pending'.
// default: false
send_pending_confirmation_email?: boolean;
}
type Response = SubscriberFor all subscribers, Hyvor Post records the lists they have previously unsubscribed from. This makes it easier to build automations around list subscriptions while respecting subscribers' preferences.
list_add_strategy_if_unsubscribed:
ignore - use this strategy for most auto-subscribing cases (e.g. automatically subscribing
a user to a list when they start a trial). This makes sures that if the user has previously unsubscribed
from the list, they will not be re-subscribed.force_add - use this strategy if the user is explicitly asking to subscribe to the list
again (e.g. they checked a checkbox to subscribe to the newsletter). This will add the subscriber
to the list even if they have previously unsubscribed.list_removal_reason:
unsubscribe - use this reason if the subscriber is explicitly asking to be removed
from the list (e.g. they unchecked a checkbox to unsubscribe). This will record an
unsubscription, blocking future re-adds unless list_add_strategy_if_unsubscribed=force_add. Hyvor Post's default unsubscribe form
uses this.other - use this reason if you want to remove the subscriber from the list without recording
an unsubscription.{
"email": "example@example.com",
"lists": ["Default"]
}{
"email": "example@example.com",
"lists": [123],
"lists_strategy": "add"
}{
"email": "example@example.com",
"lists": ["Paid Users"],
"lists_strategy": "remove",
// unsubscribe, bounce, or other
"list_removal_reason": "unsubscribe"
}{
"email": "example@example.com",
"lists": ["Default"],
"status": "pending",
"send_pending_confirmation_email": true
}{
"email": "example@example.com",
"lists": ["Default"],
"lists_strategy": "add",
// ignore unsubscription if the subscriber was removed from the list due to a bounce
// but allow re-adding if they previously unsubscribed themselves
"list_skip_resubscribe_on": ["bounce"]
}To force re-adding both previous unsubscribes and bounces, use an empty array for list_skip_resubscribe_on.
DELETE /subscribers/{id} type Request = {}
type Response = {}POST /subscribers/bulk type Request = {
subscribers_ids: number[];
action: 'delete' | 'status_change' | 'metadata_update';
status?: 'subscribed' | 'unsubscribed' | 'pending'; // required if action is status_change
metadata?: Record<string, string>; // required if action is metadata_update
}
type Response = {
status: string;
message: string;
subscribers: Subscriber[];
}Subscriber metadata definitions allow you to define custom fields for subscribers. These fields can be used to store additional information about subscribers.
Endpoints:
POST /subscriber-metadata-definitions - Create a subscriber metadata definitionPATCH /subscriber-metadata-definitions/{id} - Update a subscriber metadata definitionDELETE /subscriber-metadata-definitions/{id} - Delete a subscriber metadata definitionObjects:
POST /subscriber-metadata-definitions type Request = {
key: string; // max length: 255
name: string; // max length: 255
}
type Response = SubscriberMetadataDefinitionkey can only contain lowercase letters, numbers, and underscores.key cannot be changed.PATCH /subscriber-metadata-definitions/{id} type Request = {
name: string; // max length: 255
}
type Response = SubscriberMetadataDefinitionDELETE /subscriber-metadata-definitions/{id} type Request = {}
type Response = {}Endpoints:
GET /sending-profiles - Get sending profilesPOST /sending-profiles - Create a sending profilePATCH /sending-profiles/{id} - Update a sending
profileDELETE /sending-profiles/{id} - Delete a sending
profileObjects:
GET /sending-profiles type Request = {}
type Response = SendingProfile[]POST /sending-profiles type Request = {
from_email: string;
from_name?: string | null;
reply_to_email?: string | null;
brand_name?: string | null;
brand_logo?: string | null; // a publicly accessible URL of the logo
brand_url?: string | null;
}
type Response = SendingProfilePATCH /sending-profiles/{id} type Request = {
from_email?: string;
from_name?: string | null;
reply_to_email?: string | null;
brand_name?: string | null;
brand_logo?: string | null; // a publicly accessible URL of the logo
brand_url?: string | null;
is_default?: true;
}
type Response = SendingProfileDELETE /sending-profiles/{id} type Request = {}
type Response = {}Endpoints:
GET /templates - Get newsletter templatePATCH /templates - Update newsletter templatePOST /templates/render - Render newsletter template with
contentObjects:
GET /templates type Request = {}
type Response = TemplatePATCH /templates type Request = {
template?: string;
}
type Response = TemplatePOST /templates/render type Request = {
template?: string | null;
}
type Response = {
html: string;
}The owner of the newsletter can invite other users as Admins to collaborate on managing the newsletter.
Endpoints:
GET /users - Get userDELETE /users/{id} - Delete userGET /invites - Get invitesPOST /invites - Create an inviteDELETE /invites/{id} - Delete an inviteObjects:
GET /users type Request = {}
type Response = User[]DELETE /users/{id} type Request = {}
type Response = {}GET /invites type Request = {}
type Response = UserInvite[]POST /invites You must ask your Admins to create a HYVOR account before sending an invitation.
type Request = {
username?: string;
email?: string;
}
type Response = UserInviteusername or email of the invitee's HYVOR account is required.DELETE /invites/{id} type Request = {}
type Response = {}Endpoints:
POST /media - Upload mediaObjects:
POST /media type Request = {
// max size: 10MB
// supported formats: jpg, jpeg, png, gif, webp
file: File;
folder: 'issue_images' | 'newsletter_images';
}
type Response = MediaEndpoints:
GET /export - Get subscriber exportsPOST /export - Create a subscriber exportObjects:
GET /export type Request = {}
type Response = SubscriberExport[]POST /export type Request = {}
type Response = SubscriberExportinterface Newsletter {
id: string;
subdomain: string;
created_at: number; // unix timestamp
name: string;
address: string | null;
unsubscribe_text: string | null;
branding: boolean;
template_color_accent: string | null;
template_color_accent_text: string | null;
template_color_background: string | null;
template_color_background_text: string | null;
template_color_box: string | null;
template_color_box_text: string | null;
template_box_shadow: string | null;
template_box_radius: string | null;
template_box_border: string | null;
template_font_family: string | null;
template_font_size: string | null;
template_font_weight: string | null;
template_font_weight_heading: string | null;
template_font_line_height: string | null;
form_title: string | null;
form_description: string | null;
form_footer_text: string | null;
form_button_text: string | null;
form_success_message: string | null;
form_width: number | null; // null = 100%
form_custom_css: string | null;
form_color_light_text: string | null; // null = inherit
form_color_light_text_light: string | null;
form_color_light_accent: string | null;
form_color_light_accent_text: string | null;
form_color_light_input: string | null;
form_color_light_input_text: string | null;
form_light_input_box_shadow: string | null;
form_light_input_border: string | null;
form_light_border_radius: number | null;
form_color_dark_text: string | null; // null = inherit
form_color_dark_text_light: string | null;
form_color_dark_accent: string | null;
form_color_dark_accent_text: string | null;
form_color_dark_input: string | null;
form_color_dark_input_text: string | null;
form_dark_input_box_shadow: string | null;
form_dark_input_border: string | null;
form_dark_border_radius: number | null;
form_default_color_palette: 'light' | 'dark' | 'os';
form_input_border_radius: number;
}interface Issue {
id: number;
uuid: string;
created_at: number; // unix timestamp
subject: string | null;
content: string | null;
sending_profile_id: number;
status: 'draft' | 'scheduled' | 'sending' | 'sent';
lists: number[];
scheduled_at: number | null; // unix timestamp
sending_at: number | null; // unix timestamp
sent_at: number | null; // unix timestamp
total_sends: number;
from_email: string | null;
from_name: string | null;
reply_to_email: string | null;
sendable_subscribers_count: number;
}interface Send {
id: number;
created_at: number; // unix timestamp
subscriber: Subscriber | null;
email: string;
status: 'pending' | 'sent' | 'failed';
sent_at: number | null; // unix timestamp
failed_at: number | null; // unix timestamp
delivered_at: number | null; // unix timestamp
unsubscribed_at: number | null; // unix timestamp
bounced_at: number | null; // unix timestamp
hard_bounce: boolean;
complained_at: number | null; // unix timestamp
}interface List {
id: number;
created_at: number; // unix timestamp
name: string;
description: string | null;
subscribers_count: number;
}interface Subscriber {
id: number;
email: string;
source: 'console' | 'form' | 'import';
status: 'subscribed' | 'pending';
list_ids: number[];
lists: string[]; // list names
subscribe_ip: string | null;
is_opted_in: boolean;
subscribed_at: number | null; // unix timestamp
metadata: Record<string, string>;
}interface SubscriberMetadataDefinition {
id: number;
created_at: number; // unix timestamp
key: string;
name: string;
type: 'text'; // only 'text' is currently supported
}interface SendingProfile {
id: number;
created_at: number; // unix timestamp
from_email: string;
from_name: string | null;
reply_to_email: string | null;
brand_name: string | null;
brand_logo: string | null;
brand_url: string | null;
is_default: boolean;
is_system: boolean;
}interface Template {
template: string;
}interface UserMiniObject {
name: string;
email: string;
username: string | null;
picture_url: string | null;
}interface User {
id: number;
role: 'owner' | 'admin';
created_at: number; // unix timestamp
user: UserMiniObject;
}interface UserInvite {
id: number;
created_at: number; // unix timestamp
role: 'admin';
user: UserMiniObject;
expires_at: number; // unix timestamp
}interface Media {
id: number;
created_at: number; // unix timestamp
folder: 'issue_images' | 'newsletter_images' | 'import' | 'export';
url: string;
size: number; // in bytes
extension: string;
}interface SubscriberExport {
id: number;
created_at: number; // unix timestamp
status: 'pending' | 'completed' | 'failed';
error_message: string | null;
url: string | null;
}