API Console

L’API Console vous permet d’automatiser les tâches liées à votre newsletter via HTTP, avec une authentification par clé API. C’est la même API que nous utilisons en interne dans la Console.

Premiers pas

  • CrĂ©ez une clĂ© API Console dans Console → Paramètres → ClĂ©s API. Chaque clĂ© doit recevoir un ou plusieurs scopes, qui limitent ce Ă  quoi elle peut accĂ©der.
  • L’URL de base : https://post.hyvor.com/api/console
  • Pour chaque requĂŞte, dĂ©finissez l’en-tĂŞte Authorization sur Bearer <API_KEY>.
  • MĂ©thodes HTTP disponibles :
    • GET - RĂ©cupĂ©rer une ressource
    • POST - CrĂ©er une ressource ou effectuer une action
    • PUT - Mettre Ă  jour une ressource
    • DELETE - Supprimer une ressource
  • Les paramètres de requĂŞte peuvent ĂŞtre envoyĂ©s en JSON (recommandĂ©) ou en application/x-www-form-urlencoded.
  • Tous les endpoints renvoient des donnĂ©es JSON. La rĂ©ponse est un objet ou un tableau d’objets.

Dans cette documentation, tous les objets, paramètres de requête et réponses sont écrits sous forme d'interfaces TypeScript afin de rendre les déclarations de types concises.

Catégories

Les endpoints de l’API Console sont classés selon la ressource avec laquelle ils interagissent.

Aller à une catégorie :

Newsletter

Endpoints :

Objets :

Récupérer les données de la newsletter

GET /newsletter

type Request = {}
type Response = Newsletter

Mettre Ă  jour une newsletter

PATCH /newsletter

type Request = Partial<Newsletter>  // except id, created_at
type Response = Newsletter

Supprimer une newsletter

DELETE /newsletter

type Request = {}
type Response = {}
Cet endpoint effectue une suppression logique de la newsletter et programme sa suppression définitive après 30 jours.

Numéro

Endpoints :

Objets :

Récupérer les numéros

GET /issues

type Request = {
    limit?: number; // default: 50
    offset?: number; // default: 0
}
type Response = Issue[]

Créer un numéro

POST /issues

type Request = {}
type Response = Issue

Récupérer un numéro

GET /issues/{id}

type Request = {}
type Response = Issue

Mettre à jour un numéro

PATCH /issues/{id}

type Request = {
    subject?: string;
    lists?: number[];
    content?: string;
    sending_profile_id?: number;
}
type Response = Issue

Supprimer un numéro

DELETE /issues/{id}

type Request = {}
type Response = {}

Envoyer un numéro

POST /issues/{id}/send

type Request = {}
type Response = Issue

Prévisualiser un numéro

Génère l’aperçu HTML d’un numéro et renvoie le nombre d’abonnés auxquels il pourrait être envoyé.

GET /issues/{id}/preview

type Request = {}
type Response = {
    html: string;
    sendable_subscribers_count: number;
}

Récupérer la progression de l'envoi d'un numéro

Récupère la progression de l’envoi d’un numéro en cours d’envoi.

GET /issues/{id}/progress

type Request = {}
type Response = {
    total: number;
    sent: number;
    progress: number; // percentage, 0-100
} | null // null if the issue has no sends yet

Récupérer les envois d'un numéro

GET /issues/{id}/sends

type Request = {
    limit?: number; // default: 50
    offset?: number; // default: 0
    search?: string;
    type?: string;
}
type Response = Send[]

Récupérer le rapport d'un numéro

Récupère le nombre de distributions, d’ouvertures, de clics, de rebonds et de plaintes d’un numéro.

GET /issues/{id}/report

type Request = {}
type Response = {
    counts: {
        total: number;
        pending: number;
        sent: number;
        failed: number;
        unsubscribed: number;
        bounced: number;
        complained: number;
    }
}

Listes

Endpoints :

Objets :

Créer une liste

POST /lists

type Request = {
    name: string;   // max length: 255
    description?: string;
}
type Response = List

Mettre Ă  jour une liste

PATCH /lists/{id}

type Request = {
    name?: string;   // max length: 255
    description?: string;
}
type Response = List

Supprimer une liste

DELETE /lists/{id}

type Request = {}
type Response = {}

Abonné

Endpoints :

Objets :

Récupérer les abonnés

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[]

Récupérer un abonné par e-mail

GET /subscribers/email/{email}

type Request = {}
type Response = Subscriber // 404 if not found

Créer ou mettre à jour un abonné

POST /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 skipping the re-subscription to that list.
    // see below for more info
    // default: ['unsubscribe', 'bounce', 'complaint']
    list_skip_resubscribe_on?: ('unsubscribe' | 'bounce' | 'complaint' | 'other')[];

    // 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' | 'complaint' | '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 = Subscriber
Gérer les désabonnements et réabonnements aux listes

Pour chaque abonné, Hyvor Post enregistre les listes dont il s’est précédemment désabonné. Cela facilite la création d’automatisations autour des abonnements aux listes, tout en respectant les préférences des abonnés.

list_skip_resubscribe_on : lors de l’ajout d’un abonné existant à une liste dont il a été retiré auparavant, ce paramètre détermine lesquels des motifs de retrait ci-dessous doivent bloquer le réajout. Par défaut, les désabonnements, rebonds et plaintes précédents bloquent tous le réajout ; passez un tableau vide pour toujours réajouter l’abonné, quelle que soit la raison de son départ.

list_removal_reason :

  • unsubscribe - utilisez ce motif si l’abonnĂ© demande explicitement Ă  ĂŞtre retirĂ© de la liste (par exemple, il a dĂ©cochĂ© une case pour se dĂ©sabonner). Un dĂ©sabonnement est alors enregistrĂ©, ce qui bloque les rĂ©ajouts futurs, sauf si le paramètre list_skip_resubscribe_on de la demande de rĂ©ajout exclut unsubscribe. Le formulaire de dĂ©sabonnement par dĂ©faut de Hyvor Post utilise ce motif.
  • bounce - enregistrĂ© automatiquement lorsqu’un envoi Ă  l’abonnĂ© provoque un rebond dĂ©finitif.
  • complaint - enregistrĂ© automatiquement lorsque l’abonnĂ© signale un envoi comme spam.
  • other - utilisez ce motif si vous souhaitez retirer l’abonnĂ© de la liste sans l’enregistrer sous l’un des motifs ci-dessus (ne bloque pas les rĂ©ajouts futurs par dĂ©faut).
Exemples
Cet exemple crée un nouvel abonné inscrit à la liste « Default ». Si un abonné avec la même adresse e-mail existe déjà, il est mis à jour et ses listes sont remplacées par « Default » uniquement (les listes existantes sont écrasées).
{
    "email": "example@example.com",
    "lists": ["Default"]
}
En supposant que vous ayez une liste avec l'ID 123, cet exemple ajoute l'abonné à cette liste sans modifier ses autres abonnements. Si l'abonné est déjà inscrit à la liste, rien n'est modifié.
{
    "email": "example@example.com",
    "lists": [123],
    "lists_strategy": "add"
}
Cet exemple retire simplement l'abonné de la liste nommée « Paid Users ».
{
    "email": "example@example.com",
    "lists": ["Paid Users"],
    "lists_strategy": "remove",

    // unsubscribe, bounce, or other
    "list_removal_reason": "unsubscribe"
}
Cet exemple crée un abonné ou met à jour un abonné existant avec le statut « pending », puis lui envoie un e-mail lui demandant de confirmer son abonnement.
{
    "email": "example@example.com",
    "lists": ["Default"],
    "status": "pending",
    "send_pending_confirmation_email": true
}
Par défaut, cet endpoint ignore les tentatives de réabonnement aux listes dont l'abonné s'est désabonné (ou dont il a été retiré suite à un rebond). Cet exemple montre comment modifier ce comportement.
{
    "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"]
}

Pour forcer le réajout malgré les désabonnements et les rebonds précédents, utilisez un tableau vide pour list_skip_resubscribe_on.

Renvoyer l'e-mail de confirmation d'inscription

Renvoie l’e-mail de confirmation d’inscription à un abonné en attente.

POST /subscribers/{id}/resend-opt-in

type Request = {}
type Response = {}

Supprimer un abonné

DELETE /subscribers/{id}

type Request = {}
type Response = {}

Mettre à jour des abonnés en masse

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[];
}

Métadonnées d'abonné

Les définitions de métadonnées d’abonné vous permettent de définir des champs personnalisés pour les abonnés. Ces champs servent à stocker des informations supplémentaires sur les abonnés.

Endpoints :

Objets :

Créer une définition de métadonnée d'abonné

POST /subscriber-metadata-definitions

type Request = {
    key: string;   // max length: 255
    name: string;  // max length: 255
}
type Response = SubscriberMetadataDefinition
  • key ne peut contenir que des lettres minuscules, des chiffres et des tirets bas.
  • Une fois créée, la key ne peut plus ĂŞtre modifiĂ©e.

Mettre à jour une définition de métadonnée d'abonné

PATCH /subscriber-metadata-definitions/{id}

type Request = {
    name: string;  // max length: 255
}
type Response = SubscriberMetadataDefinition

Supprimer une définition de métadonnée d'abonné

DELETE /subscriber-metadata-definitions/{id}

type Request = {}
type Response = {}

Profil d'envoi

Endpoints :

Objets :

Récupérer les profils d'envoi

GET /sending-profiles

type Request = {}
type Response = SendingProfile[]

Créer un profil d'envoi

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 = SendingProfile

Mettre Ă  jour un profil d'envoi

PATCH /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?: boolean;
}
type Response = SendingProfile

Supprimer un profil d'envoi

Le profil d’envoi système ne peut pas être supprimé.

DELETE /sending-profiles/{id}

type Request = {}
type Response = SendingProfile[] // the newsletter's remaining sending profiles

Modèle

Hyvor Post propose un système de modèles de newsletter flexible qui vous permet de personnaliser l’apparence de vos newsletters.

Endpoints :

Objets :

Récupérer le modèle de la newsletter

GET /templates

type Request = {}
type Response = Template

Mettre à jour le modèle de la newsletter

PATCH /templates

type Request = {
    template?: string;
}
type Response = Template

Générer le modèle de la newsletter avec du contenu

POST /templates/render

type Request = {
    template?: string | null;
}
type Response = {
    html: string;
}

Utilisateur

Les administrateurs de l’organisation propriétaire de la newsletter peuvent être ajoutés comme utilisateurs pour collaborer à sa gestion.

Endpoints :

Objets :

Récupérer les utilisateurs

GET /users

type Request = {}
type Response = User[]

Créer un utilisateur

POST /users

L’utilisateur doit déjà être membre de l’organisation propriétaire de cette newsletter.

type Request = {
    user_id: number; // the user's id in HYVOR (AuthInterface)

    // what to do if the user is already added to the newsletter
    // throw: return a 400 error (default)
    // ignore: return the existing user without an error
    on_duplicate?: 'throw' | 'ignore';
}
type Response = User
  • Renvoie une erreur 400 si l'utilisateur n'est pas membre de l'organisation propriĂ©taire de cette newsletter.

Supprimer un utilisateur

DELETE /users

type Request = {
    // one of user_id or id is required
    user_id?: number; // the user's id in HYVOR (AuthInterface)
    id?: number; // the user's id in this newsletter's user list
}
type Response = {}

Clés API

Chaque clé API reçoit un ou plusieurs scopes, qui limitent les ressources et les actions auxquelles elle peut accéder. Scopes disponibles :

  • newsletter.read / newsletter.write / newsletter.delete
  • issues.read / issues.write
  • sending_profiles.read / sending_profiles.write
  • subscribers.read / subscribers.write
  • users.read / users.write
  • templates.read / templates.write
  • api_keys.read / api_keys.write
  • media.write
  • data.read / data.write - listes d’abonnĂ©s, importations et exportations

Endpoints :

Objets :

Récupérer les clés API

La clé brute n’est pas renvoyée ; seules ses métadonnées le sont.

GET /api-keys

type Request = {}
type Response = ApiKey[]

Créer une clé API

POST /api-keys

type Request = {
    name: string;   // max length: 255
    scopes: string[]; // see the list of scopes above
}
type Response = ApiKey
La propriété key (la clé brute) n'est renvoyée qu'une seule fois, à la création. Conservez-la en lieu sûr : elle ne pourra plus être récupérée.

Mettre à jour une clé API

PATCH /api-keys/{id}

type Request = {
    name?: string;   // max length: 255
    is_enabled?: boolean;
    scopes?: string[];
}
type Response = ApiKey

Régénérer une clé API

Régénère la clé brute d’une clé API. L’ancienne clé est invalidée immédiatement et la nouvelle clé brute n’est renvoyée qu’une seule fois.

POST /api-keys/{id}

type Request = {}
type Response = ApiKey

Supprimer une clé API

Les requêtes effectuées avec la clé supprimée seront rejetées immédiatement.

DELETE /api-keys/{id}

type Request = {}
type Response = {}

Médias

Endpoints :

Objets :

Téléverser un média

POST /media

type Request = {
    // max size: 10MB
    // supported formats: jpg, jpeg, png, gif, webp
    file: File;
    folder: 'issue_images' | 'newsletter_images';
}
type Response = Media

Exportation

Endpoints :

  • GET /export - RĂ©cupĂ©rer les exportations d’abonnĂ©s
  • POST /export - CrĂ©er une exportation d’abonnĂ©s

Objets :

Récupérer les exportations d'abonnés

GET /export

type Request = {}
type Response = SubscriberExport[]

Créer une exportation d'abonnés

POST /export

type Request = {}
type Response = SubscriberExport

Objets

Objet Newsletter

interface Newsletter {
    id: string;
    subdomain: string;
    created_at: number; // unix timestamp
    name: string;
    language_code: string | null;
    is_rtl: boolean;
    metadata: Record<string, 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;
}

Objet Issue

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;
}

Objet Send

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
}

Objet List

interface List {
    id: number;
    created_at: number; // unix timestamp
    name: string;
    description: string | null;
    subscribers_count: number;
}

Objet Subscriber

interface Subscriber {
    id: number;
    email: string;
    source: 'console' | 'form' | 'import';
    status: 'subscribed' | 'pending';
    list_ids: number[];
    lists: string[]; // list names
    subscribe_ip: string | null;
    subscribed_at: number | null; // unix timestamp
    metadata: Record<string, string>;
}

Objet SubscriberMetadataDefinition

interface SubscriberMetadataDefinition {
    id: number;
    created_at: number; // unix timestamp
    key: string;
    name: string;
    type: 'text'; // only 'text' is currently supported
}

Objet SendingProfile

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;
}

Objet Template

interface Template {
    template: string;
}

Objet UserMini

interface UserMiniObject {
    name: string;
    email: string;
    username: string | null;
    picture_url: string | null;
}

Objet User

interface User {
    id: number;
    role: 'owner' | 'admin';
    created_at: number; // unix timestamp
    user: UserMiniObject;
}

Objet Media

interface Media {
    id: number;
    created_at: number; // unix timestamp
    folder: 'issue_images' | 'newsletter_images' | 'import' | 'export';
    url: string;
    size: number; // in bytes
    extension: string;
}

Objet SubscriberExport

interface SubscriberExport {
    id: number;
    created_at: number; // unix timestamp
    status: 'pending' | 'completed' | 'failed';
    error_message: string | null;
    url: string | null;
}