Skip to content
ABUNA
DocumentationÉvénements

Référence de l'API

Événements

Le journal de tout ce qui s'est passé dans une application, et chaque type d'événement.

Un événement enregistre quelque chose qui s'est produit dans votre application, comme une facture payée. Abuna enregistre chaque événement, et l'envoie aussi à votre URL de webhook si vous en avez défini une.

L'objet event

Un événement a la même forme ici et dans un corps de webhook. Un webhook pour l'événement porte le même id.

Attributs

  • idstring

    Identifiant unique de l'événement. Les ID d'événement se trient dans l'ordre où les événements ont eu lieu. Voir Ordre des événements.
  • typestring

    Le type de l'événement.
  • environmentstring

    test ou live.
  • created_attimestamp

    Quand l'événement a eu lieu, en secondes Unix.
  • dataobject

    Les données de l'événement. Leurs champs dépendent du type.
L'objet event
{
  "created_at": 1791143442,
  "data": {
    "amount": 5000,
    "credit": 0,
    "currency": "XAF",
    "invoice_id": "hUPYtkyo",
    "invoice_number": "ACME-000001",
    "kind": "period",
    "metadata": {
      "user_id": "42"
    },
    "paid_at": 1791143442,
    "period_end": 1793821842,
    "period_id": "tKhKXYjr",
    "period_start": 1791143442,
    "subscription": {
      "activated_at": 1791143442,
      "app_id": "VyYjSLf1",
      "cancel_at": null,
      "canceled_at": null,
      "created_at": 1791143442,
      "current_period_end": 1793821842,
      "current_period_start": 1791143442,
      "customer_id": "OljRiqBE",
      "ended_reason": null,
      "id": "coRNVNUz",
      "manage_token": "d6f6ca3a8b285784aed37bd593bab9dea12e",
      "metadata": {
        "user_id": "42"
      },
      "price_id": "UKjuDVyN",
      "starts_at": 1791143442,
      "status": "active"
    },
    "subscription_id": "coRNVNUz"
  },
  "environment": "test",
  "id": "01M447FXSP98350QH3TKE3YG6D",
  "type": "invoice.paid"
}

Lister les événements

GET/v1/apps/{appID}/events

Liste les événements de l'application, du plus récent au plus ancien, une page à la fois. Chaque page continue avec des événements plus anciens. Voir Listes et filtres.

Paramètres de requête

  • typestring

    Uniquement les événements de ce type, comme invoice.paid.
  • limitinteger

    Combien d'événements renvoyer, de 1 à 100. Par défaut : 50.
  • cursorstring

    La valeur next_cursor de la page précédente. Envoyez les mêmes filtres avec.

Attributs de la réponse

  • itemsarray

    Les événements de la page, du plus récent au plus ancien. Vide quand rien ne correspond.
  • next_cursorstringnullable

    Passez-le comme cursor pour obtenir la page suivante, plus ancienne. Il vaut null sur la dernière page. Traitez-le comme opaque.

Renvoie 200. Un limit en dehors de 1 à 100 renvoie 422 avec le code invalid et un élément errors pour limit.

GET /v1/apps/{appID}/events
curl "https://api.abuna.app/v1/apps/8ddhXCDW/events?type=invoice.paid" \
  -H "Authorization: Bearer sk_test_..."
Réponse
{
  "items": [
    {
      "created_at": 1791143442,
      "data": {
        "amount": 5000,
        "credit": 0,
        "currency": "XAF",
        "invoice_id": "hUPYtkyo",
        "invoice_number": "ACME-000001",
        "kind": "period",
        "metadata": {
          "user_id": "42"
        },
        "paid_at": 1791143442,
        "period_end": 1793821842,
        "period_id": "tKhKXYjr",
        "period_start": 1791143442,
        "subscription": {
          "activated_at": 1791143442,
          "app_id": "VyYjSLf1",
          "cancel_at": null,
          "canceled_at": null,
          "created_at": 1791143442,
          "current_period_end": 1793821842,
          "current_period_start": 1791143442,
          "customer_id": "OljRiqBE",
          "ended_reason": null,
          "id": "coRNVNUz",
          "manage_token": "d6f6ca3a8b285784aed37bd593bab9dea12e",
          "metadata": {
            "user_id": "42"
          },
          "price_id": "UKjuDVyN",
          "starts_at": 1791143442,
          "status": "active"
        },
        "subscription_id": "coRNVNUz"
      },
      "environment": "test",
      "id": "01M447FXSP98350QH3TKE3YG6D",
      "type": "invoice.paid"
    }
  ],
  "next_cursor": "MDFKOVpROEUxRjJHM0g0SjVLNk03TjhQOVE"
}

Récupérer un événement

GET/v1/apps/{appID}/events/{id}

Récupère un événement, dans la même forme que le corps de son webhook. Utilisez-le pour récupérer à nouveau un événement par l'id qu'un webhook vous a donné.

Renvoie l'événement. Si l'application n'a aucun événement avec cet ID, renvoie 404 avec le code not_found.

GET /v1/apps/{appID}/events/{id}
curl https://api.abuna.app/v1/apps/8ddhXCDW/events/01M447FXSP98350QH3TKE3YG6D \
  -H "Authorization: Bearer sk_test_..."
Réponse
{
  "created_at": 1791143442,
  "data": {
    "amount": 5000,
    "credit": 0,
    "currency": "XAF",
    "invoice_id": "hUPYtkyo",
    "invoice_number": "ACME-000001",
    "kind": "period",
    "metadata": {
      "user_id": "42"
    },
    "paid_at": 1791143442,
    "period_end": 1793821842,
    "period_id": "tKhKXYjr",
    "period_start": 1791143442,
    "subscription": {
      "activated_at": 1791143442,
      "app_id": "VyYjSLf1",
      "cancel_at": null,
      "canceled_at": null,
      "created_at": 1791143442,
      "current_period_end": 1793821842,
      "current_period_start": 1791143442,
      "customer_id": "OljRiqBE",
      "ended_reason": null,
      "id": "coRNVNUz",
      "manage_token": "d6f6ca3a8b285784aed37bd593bab9dea12e",
      "metadata": {
        "user_id": "42"
      },
      "price_id": "UKjuDVyN",
      "starts_at": 1791143442,
      "status": "active"
    },
    "subscription_id": "coRNVNUz"
  },
  "environment": "test",
  "id": "01M447FXSP98350QH3TKE3YG6D",
  "type": "invoice.paid"
}

Ordre des événements

Les ID d'événement se trient dans l'ordre où les événements ont eu lieu. Comparez-les comme de simples chaînes : l'ID d'un événement plus tardif se trie après celui d'un événement plus ancien. created_at est en secondes entières, donc les événements d'un même changement le partagent souvent. Ordonnez les événements par id, pas par created_at.

Un même changement enregistre plusieurs événements à la fois. Ils arrivent dans cet ordre :

  • Nouvel abonnement : subscription.created, puis invoice.created.
  • Renouvellement : subscription.updated si un passage à un tarif inférieur programmé commence avec la nouvelle période, subscription.renewed, subscription.past_due si la nouvelle période a déjà commencé, puis invoice.created. Un renouvellement émis avant la fin de la période payée envoie subscription.past_due de son côté plus tard, à la fin de la période payée, s'il est encore impayé à ce moment-là.
  • Paiement : subscription.updated s'il paie une montée de formule, session.completed s'il a été payé via une session payment ou termine une session plan_change, invoice.paid, puis au premier paiement subscription.paid et le session.completed du checkout, et enfin subscription.recovered si l'abonnement était past_due.

Rattraper les événements manqués

Si votre serveur a manqué des webhooks, listez les événements que vous n'avez pas traités et traitez-les maintenant. Commencez sans cursor, puis passez le next_cursor de chaque page jusqu'à ce qu'il soit null, ou jusqu'à atteindre des événements créés 5 minutes avant le plus récent que vous avez traité. Un événement peut être listé quelques secondes après un plus récent, donc ignorez ceux que vous avez déjà traités par id plutôt que de vous arrêter au premier. Traitez ce que vous avez collecté du plus ancien au plus récent.

Rattraper les événements manqués
const base = "https://api.abuna.app/v1/apps/8ddhXCDW/events?limit=100";
const headers = { Authorization: "Bearer sk_test_..." };
const newestHandledAt = 1727600600; // created_at of the newest event you handled
const stopAt = newestHandledAt - 5 * 60;

const missed = [];
let cursor = null;
let done = false;
while (!done) {
  const url = base + (cursor ? "&cursor=" + encodeURIComponent(cursor) : "");
  const page = await (await fetch(url, { headers })).json();
  for (const event of page.items) {
    if (event.created_at < stopAt) {
      done = true;
      break;
    }
    if (!(await alreadyHandled(event.id))) missed.push(event);
  }
  cursor = page.next_cursor;
  if (!cursor) done = true;
}

// Oldest first, in the order the events happened.
for (const event of missed.reverse()) {
  await handle(event);
}

Types d'événement

Chaque type ci-dessous liste les champs de son data. Les ID sont des chaînes, les montants sont des entiers dans la plus petite unité de la devise, et les heures sont en secondes Unix.

Chaque événement dont le data contient un subscription_id contient aussi subscription : l'objet subscription complet, tel que Récupérer un abonnement le renvoie sous subscription, tel qu'il est juste après le changement que l'événement signale. Un événement de session dont le subscription_id est null n'a pas de subscription. Les listes ci-dessous omettent subscription.

Les événements d'abonnement, de facture et d'offre de formule portent les métadonnées de l'abonnement au moment où l'événement est enregistré. Les événements de session portent lemetadata propre à la session, pas celui de l'abonnement. Passez du metadata à chaque session que vous créez. Sans cela, les événements de session portent {}. Les listes ci-dessous omettent le metadata sauf indication contraire.

session.completed

Une session s'est terminée. Une session se termine au plus une fois, et une session expirée ne se termine jamais. Le data dépend du type de la session. Chaque type a session_id, type, subscription_id, customer_id, metadata (le metadata que vous avez passé à la création de la session) et completed_at.

  • checkout (Créer une session checkout) : le premier paiement a réussi et l'abonnement est maintenant active. subscription.paid est enregistré au même moment. Ajoute price_id, le tarif de l'abonnement.
  • payment (Créer une session de paiement) : la facture a été payée via la session. invoice.paid est aussi envoyé. Ajoute invoice_id.
  • plan_change (Créer une session de changement de formule) : le client a confirmé le changement, et l'a payé s'il s'agissait d'une montée de formule avec quelque chose à payer. Ajoute les champs ci-dessous.
  • cancel (Créer une session d'annulation) : le client a confirmé l'annulation, programmée ou immédiate. subscription.updated (programmée) ou subscription.canceled (immédiate) est aussi envoyé. Ajoute les champs ci-dessous.
  • portal (Créer une session d'espace client) : le client a ouvert son espace client pour la première fois. Cela ne signifie pas qu'il a changé quelque chose. Ce qu'il y fait envoie ses propres événements, comme subscription.updated, subscription.canceled, invoice.paid et customer.updated. N'ajoute rien.

plan_change data ajoutés

  • plan_offer_idstring

    L'offre de formule que la session a créée.
  • previous_price_idstring

    Le tarif avant le changement.
  • price_idstring

    Le nouveau tarif.
  • directionstring

    upgrade ou downgrade.
  • effective_attimestamp

    Quand le nouveau tarif commence. Pour un passage à un tarif inférieur, la fin de la période payée : subscription.updated se déclenche alors avec le changement de tarif.

cancel data ajoutés

  • cancel_attimestampnullable

    Quand l'abonnement prend fin. subscription.canceled se déclenche alors. Null quand il a pris fin tout de suite.
  • immediateboolean

    true quand l'abonnement a pris fin tout de suite.

session.expired

Une session ouverte a expiré à son expires_at, ou vous l'avez fait expirer. Abuna ne fait pas expirer une session checkout de lui-même pendant que son paiement est en attente. Un abonnement pending démarré par une session checkout reste, et prend fin avec checkout_expired s'il n'est pas payé à temps.

Une session payment expire aussi quand sa facture est payée autrement, annulée, ou quand son abonnement prend fin. Une session plan_change expire aussi quand son offre est remplacée, annulée ou expirée, ou quand une montée de formule qu'elle a démarrée tombe en désuétude faute de paiement. Une session cancel expire 24 heures après sa création si le client ne confirme pas, et une session portal 1 heure après sa création si personne ne l'ouvre. Voir Types de session.

data

  • session_idstring

    La session.
  • typestring

    Le type de la session : checkout, payment, plan_change, cancel ou portal.
  • subscription_idstringnullable

    L'abonnement que la session a démarré ou sur lequel elle agit. Pour checkout, null si le client n'est pas allé jusque-là.
  • customer_idstringnullable

    Le client que vous avez passé, ou celui créé à partir des coordonnées du client. Null si aucun des deux.
  • price_idstringnullable

    Le tarif de la session, ou null pour les sessions payment, cancel et portal.
  • invoice_idstringnullable

    La facture de la session payment, ou null pour les autres types.
  • plan_offer_idstringnullable

    L'offre de la session plan_change, ou null pour les autres types.
  • metadataobject

    Le metadata que vous avez passé à la création de la session.
  • expired_attimestamp

    Quand la session a expiré.
session.expired data
{
  "customer_id": "OljRiqBE",
  "expired_at": 1791143442,
  "invoice_id": null,
  "metadata": {
    "user_id": "42"
  },
  "plan_offer_id": null,
  "price_id": null,
  "session_id": "ErXGrkep",
  "subscription": {
    "activated_at": 1791143442,
    "app_id": "VyYjSLf1",
    "cancel_at": null,
    "canceled_at": null,
    "created_at": 1791143442,
    "current_period_end": 1793821842,
    "current_period_start": 1791143442,
    "customer_id": "OljRiqBE",
    "ended_reason": null,
    "id": "coRNVNUz",
    "manage_token": "d6f6ca3a8b285784aed37bd593bab9dea12e",
    "metadata": {
      "user_id": "42"
    },
    "price_id": "UKjuDVyN",
    "starts_at": 1791143442,
    "status": "active"
  },
  "subscription_id": "coRNVNUz",
  "type": "cancel"
}

subscription.created

Un abonnement a été créé, via l'API, une session checkout ou un lien de paiement. Il est encore pending.

data

  • subscription_idstring

    Le nouvel abonnement.
  • customer_idstring

    Son client.
  • price_idstring

    Son tarif.
  • period_idstring

    Sa première période de facturation.

subscription.paid

La première période de facturation a été payée, donc l'abonnement est maintenant active. Les paiements suivants envoient invoice.paid, et subscription.recovered quand ils ramènent un abonnement past_due à active.

data

  • subscription_idstring

    L'abonnement.
  • customer_idstring

    Son client.
  • price_idstring

    Son tarif.
  • period_idstring

    La période de facturation payée.
  • activated_attimestamp

    Quand l'abonnement est devenu actif.

subscription.renewed

Abuna a émis la période de facturation suivante et sa facture. Cela se produit avant la fin de la période payée, d'autant de jours à l'avance que l'indique renewal_lead_days de l'application (3 par défaut), ou quand elle se termine avec 0. La nouvelle période commence quand la période payée se termine, et le client peut la payer dès maintenant. Si elle est encore impayée à la fin de la période payée, subscription.past_due suit alors.

data

  • subscription_idstring

    L'abonnement.
  • period_idstring

    La nouvelle période de facturation.
  • amountinteger

    Le montant dû pour la nouvelle période.
  • currencystring

    XAF.

subscription.past_due

La période payée a pris fin et son renouvellement n'est pas payé, donc l'abonnement est maintenant past_due. Un renouvellement payé avant la période payée ne l'envoie jamais. Gardez l'accès du client tant qu'il est past_due. Si la période n'est pas payée à sa date limite de paiement, subscription.canceled suit avec la raison non_payment.

data

  • subscription_idstring

    L'abonnement.
  • metadataobject

    Les métadonnées de l'abonnement.

subscription.recovered

La facture d'un abonnement past_due a été payée, donc il est de nouveau active. Il vient juste après invoice.paid. Il vient aussi quand un paiement qui arrive après une annulation pour non-paiement ramène l'abonnement ; voir Paiements qui arrivent en retard. Le data est le même que celui de subscription.past_due.

subscription.updated

Quelque chose a été programmé ou a changé sur un abonnement actif. Il se déclenche quand une annulation à la fin de la période payée est programmée ou annulée, quand le tarif change (une montée de formule prend effet, ou un passage à un tarif inférieur programmé commence), quand un passage à un tarif inférieur est programmé ou annulé, quand une montée de formule en attente de paiement tombe en désuétude, et quand un nouveau changement en efface un précédent. Pour une annulation programmée, subscription.canceled suit quand elle prend fin. Voir Changer de formule.

data

  • subscription_idstring

    L'abonnement.
  • price_idstring

    Le tarif sur lequel l'abonnement est actuellement.
  • cancel_attimestampnullable

    Quand l'abonnement prendra fin, si une annulation est programmée. Null sinon.
  • scheduled_price_changeobjectnullable

    Le passage à un tarif inférieur programmé pour la fin de la période payée. Null quand il n'y en a pas.
    Afficher les attributsMasquer les attributs
    • price_idstring

      Le tarif vers lequel l'abonnement passe.
    • effective_attimestamp

      Quand il passe.
  • changeobject

    Ce qui est arrivé au tarif. Présent uniquement quand le tarif a changé ou qu'un passage à un tarif inférieur a été programmé ou annulé. Une montée de formule tombée en désuétude, une annulation à la fin de la période, et une montée de formule qui commence à attendre un paiement n'en ont pas.
    Afficher les attributsMasquer les attributs
    • typestring

      price_changed, downgrade_scheduled ou downgrade_undone.
    • old_price_idstring

      Le tarif avant.
    • new_price_idstring

      Le nouveau tarif, ou celui programmé.
    • effective_attimestamp

      Quand le changement a pris effet, ou quand le changement programmé le fera.
  • updated_bystring

    merchant quand vous l'avez changé, ou customer quand le client l'a changé dans son espace client. Quand un changement de formule est exécuté plus tard (une montée de formule payée, un passage à un tarif inférieur qui commence, une montée de formule qui tombe en désuétude), c'est celui qui l'a demandé : customer depuis l'espace client, ou merchant quand il a confirmé un changement vers lequel vous l'avez envoyé.

subscription.canceled

L'abonnement a pris fin. Il se déclenche quand l'abonnement prend vraiment fin : tout de suite pour une annulation immédiate, ou à la fin de la période payée pour une annulation programmée. Programmer une annulation envoie subscription.updated à la place. Le subscription dans le data a ended_reason unpaid pour non_payment et checkout_expired, et canceled pour les autres.

data

  • subscription_idstring

    L'abonnement.
  • reasonstring

    Pourquoi il a pris fin : merchant (vous l'avez annulé), customer (le client l'a annulé dans son espace client), non_payment (un renouvellement a dépassé sa date limite de paiement) ou checkout_expired (la première période n'a jamais été payée). Une annulation programmée qui prend fin garde qui l'a programmée, merchant ou customer.

plan_offer.created

Une session de changement de formule a créé une offre de formule, ou vous avez proposé un changement depuis le tableau de bord et Abuna a envoyé le lien au client. Une session n'envoie rien au client. Renvoyer le lien depuis le tableau de bord ne le déclenche pas à nouveau. Les quatre plan_offer événements partagent le même data.

data

  • offer_idstring

    L'offre.
  • subscription_idstring

    L'abonnement.
  • price_idstring

    Le tarif proposé.
  • expires_attimestamp

    Quand l'offre cesse de fonctionner.

plan_offer.confirmed

Le client a accepté l'offre. Un passage à un tarif inférieur est maintenant programmé, ou une montée de formule est appliquée ou en attente de paiement. subscription.updated se déclenche quand le tarif change ou que le passage à un tarif inférieur est programmé.

plan_offer.canceled

L'offre a cessé avant que le client l'accepte. Le data ajoute reason : merchant quand vous l'avez annulée, ou replaced quand une nouvelle offre ou le propre changement de formule du client a pris sa place.

plan_offer.expired

L'offre a atteint son expires_at sans être acceptée.

invoice.created

Abuna a émis une facture pour une nouvelle période de facturation, ou pour une montée de formule. Une montée de formule en attente de paiement reçoit les dates de sa période quand elle est payée, et invoice.paid les porte.

data

  • invoice_idstring

    La facture.
  • invoice_numberstring

    Son numéro.
  • subscription_idstring

    Son abonnement.
  • period_idstring

    La période de facturation qu'elle facture.
  • kindstring

    period pour une période de facturation, ou plan_change pour la première période d'une montée de formule.
  • amountinteger

    Le montant dû.
  • creditinteger

    Le temps inutilisé de la période en cours déduit du nouveau tarif. 0 sur une facture period.
  • currencystring

    XAF.
  • period_starttimestamp

    Quand la période commence.
  • period_endtimestamp

    Quand la période se termine.

invoice.paid

Une facture a été payée. Un renouvellement payé avant le début de sa période est tout de même pour cette période : period_start ne bouge pas. Une facture qui a été annulée peut aussi devenir payée, quand un paiement que le client a approuvé avant son annulation aboutit ; voir Paiements qui arrivent en retard. Le data a tous les champs de invoice.created, plus un.

data

  • paid_attimestamp

    Quand elle a été payée.

invoice.payment_failed

Un paiement sur une facture a échoué. La facture reste ouverte, donc le client peut réessayer avant la date limite de paiement.

data

  • subscription_idstring

    L'abonnement.
  • period_idstring

    La période de facturation.
  • invoice_idstring

    La facture.
  • invoice_numberstring

    Son numéro.
  • provider_referencestring

    L'ID du prestataire pour le débit. Vide s'il n'en a donné aucun.
  • messagestring

    Le message du prestataire. Vide s'il n'en a donné aucun.

customer.updated

Le nom, l'e-mail ou le numéro de téléphone d'un client a changé.

data

  • customer_idstring

    Le client.
  • namestringnullable

    Son nom maintenant.
  • emailstring

    Son adresse e-mail maintenant.
  • phone_numberstring

    Son numéro de téléphone maintenant.
  • metadataobject

    Les métadonnées du client.
  • updated_bystring

    merchant quand vous l'avez changé, ou customer quand le client l'a changé dans son espace client.

price_change.scheduled

Vous avez programmé un nouveau montant pour un tarif, ou remplacé celui qui était en attente. Les trois price_change événements partagent le même data.

data

  • price_change_idstring

    Le changement.
  • price_idstring

    Le tarif.
  • amountinteger

    Le nouveau montant.
  • previous_amountinteger

    Le montant avant le changement.
  • currencystring

    XAF.
  • effective_attimestamp

    Minuit au début du jour à partir duquel le nouveau montant s'applique, dans timezone.
  • timezonestring

    Le fuseau horaire de votre application, comme Africa/Douala. C'est le timezone de l'application.
  • keep_current_subscribersboolean

    true quand toutes les personnes abonnées ce jour-là gardent l'ancien montant.

price_change.canceled

Un changement en attente s'est arrêté avant son jour. Le data ajoute reason : merchant quand vous l'avez annulé, ou replaced quand vous avez programmé un nouveau changement à sa place. Un remplacement envoie aussi price_change.scheduled.

price_change.applied

Le montant du tarif est passé au nouveau à son effective_at. L'événement arrive dans environ une minute après ce moment. À partir de là, les checkouts et les nouveaux abonnements le paient. Les abonnés actuels y passent à leur prochain renouvellement, sauf si keep_current_subscribers est true.

notification.captured

Mode Test uniquement. Abuna a écrit un avis à un client Test et l'a conservé au lieu de l'envoyer. Cet événement ne va jamais vers votre URL de webhook.

data

  • customer_idstring

    Le client.
  • kindstring

    Ce sur quoi porte l'avis. Voir types d'avis.
  • channelstring

    email ou telegram.
  • recipientstring

    L'adresse e-mail ou la conversation Telegram à laquelle il serait allé.
  • subjectstring

    La ligne d'objet.
  • textstring

    Le message, en texte brut.
  • sentboolean

    Toujours false.

Étapes suivantes