Easy Labs
SDKsNode.jsResources

Dunning

Dunning and revenue-recovery automation — methods, parameters, and examples for @easylabs/node.

Dunning controls what happens when a recurring charge fails: how often Easy Labs retries, what email goes out, what the recovery page looks like, and what the subscription / invoice transitions to once retries are exhausted. Revenue-recovery automations layer on top — event-triggered, condition-gated, multi-action workflows (e.g. "after invoice_overdue, if amount > $500, email Sales").

Methods

Dunning config

The dunning config is a singleton per company.

easy.createOrReplaceDunningConfig(body); // POST  /dunning-config
easy.getDunningConfig();                 // GET   /dunning-config
easy.updateDunningConfig(body);          // PATCH /dunning-config
await easy.createOrReplaceDunningConfig({
  retry_mode: "smart",
  smart_retry_attempts: 8,
  smart_retry_window: "2_weeks",
  bank_debit_retries_enabled: true,
  bank_debit_retry_schedule: [3, 7, 14],     // days after failure
  subscription_terminal_action: "cancel",     // "cancel" | "unpaid" | "past_due" | "pause"
  invoice_terminal_action: "uncollectible",   // "past_due" | "uncollectible"
  payment_failed_email_enabled: true,
  expiring_card_email_enabled: true,
  card_expiry_warn_days: 30,
  payment_failed_recovery_page_mode: "hosted",
  expiring_card_recovery_page_mode: "custom_link",
  expiring_card_custom_link_url: "https://app.example.com/billing/update-card",
});

Switch to retry_mode: "custom" to drive the cadence yourself with custom_retry_schedule: number[] (days after failure).

Revenue-recovery automations

easy.listRevenueRecoveryAutomations();             // GET    /revenue-recovery-automations
easy.createRevenueRecoveryAutomation(body);        // POST   /revenue-recovery-automations
easy.updateRevenueRecoveryAutomation(id, body);    // PATCH  /revenue-recovery-automations/:id
easy.deleteRevenueRecoveryAutomation(id);          // DELETE /revenue-recovery-automations/:id
easy.listRevenueRecoveryAutomationRuns(id);        // GET    /revenue-recovery-automations/:id/runs
await easy.createRevenueRecoveryAutomation({
  name: "Big-ticket overdue → Sales",
  trigger_type: "invoice_overdue",
  conditions: [
    { type: "invoice_amount", operator: "more_than", amount_cents: 50000 },
  ],
  actions: [
    {
      type: "email_team_member",
      delay_days: 0,
      recipient_email: "sales@example.com",
      note: "High-value overdue invoice — please follow up.",
    },
  ],
  active: true,
});

Object shape

DunningConfig

A single document. Highlights:

FieldTypeNotes
retry_mode"smart" | "custom"
smart_retry_attempts4 | 8When retry_mode = "smart".
smart_retry_window"1_week" | "2_weeks" | "3_weeks" | "1_month" | "2_months"
custom_retry_schedulenumber[]Days after failure, when retry_mode = "custom".
bank_debit_retries_enabled / bank_debit_retry_scheduleboolean / number[]Bank-debit-specific retry policy.
subscription_terminal_action"cancel" | "unpaid" | "past_due" | "pause"What happens when retries are exhausted.
invoice_terminal_action"past_due" | "uncollectible"
payment_failed_email_enabled / expiring_card_email_enabled / email_action_requiredboolean
card_expiry_warn_daysnumber
payment_failed_recovery_page_mode / expiring_card_recovery_page_mode"hosted" | "custom_link"
payment_failed_custom_link_url / expiring_card_custom_link_urlstring | null

Revenue-recovery automation

CreateRevenueRecoveryAutomation:

FieldType
trigger_type"invoice_due_date_upcoming" | "invoice_finalized" | "invoice_overdue" | "subscription_payment_failed" | "subscription_canceled"
conditions[]{ type: "invoice_amount" | "invoice_metadata" | "product_on_invoice"; operator?, amount_cents?, key?, value?, product_id?, product_name? }
actions[]{ type: "email_team_member" | "mark_invoice_uncollectible"; delay_days?, recipient_user_id?, recipient_email?, recipient_name?, note? }
activeboolean

The list endpoint and run records are currently typed as Record<string, unknown>[] pending a stronger schema.

Examples

Switch to custom retry cadence

await easy.updateDunningConfig({
  retry_mode: "custom",
  custom_retry_schedule: [1, 3, 7, 14, 30],
});

Disable a dunning automation without deleting it

await easy.updateRevenueRecoveryAutomation(automationId, { active: false });

Audit recent runs

const runs = await easy.listRevenueRecoveryAutomationRuns(automationId);
console.table(runs.data);

On this page