Recurring Invoices, Docker and send emails

Version

v5.10.30 (self-hosted)

Environment

Docker (official invoiceninja/invoiceninja-debian:latest image)

Checklist

  • :white_check_mark: Can you replicate the issue on the demo? → Not tested yet.
  • :white_check_mark: Have you searched existing issues? → Yes.
  • :white_check_mark: Have you inspected the logs in storage/logs/laravel.log for any errors? → No relevant errors found.

Describe the bug

Recurring invoices are being created successfully via the API, but they remain in Pending status, and no email is being sent automatically. I’m using the bulk/start API to trigger them, which works — but the invoices are still pending and require manual approval.

Also, I was unsure whether I needed to manually configure cron on the host, or whether the scheduled jobs are handled inside the Docker container.

I can receive emails from normal invoices though, the smtp is not the problem.

Steps To Reproduce

  1. Create a recurring invoice using the API (initially in draft).
  2. Use POST /api/v1/recurring_invoices/bulk with { action: "start", ids: [...] } to activate them.
  3. Invoice is generated but remains in Pending.
  4. Email is not sent.
  5. Cron job (* * * * * docker exec -u www-data debian_app_1 php artisan schedule:run) is in place, but I want to confirm it’s the correct approach.

Expected Behavior

  • The invoice should become Approved or Sent status.
  • The contact should receive the email with the invoice.
  • All of this should be automated after triggering via the API.

Additional context

  • I’ve confirmed the subscription-job, recurring-invoice-job, auto-bill-job, and reminder-job are listed when running php artisan schedule:list.
  • Running php artisan schedule:run manually returns INFO No scheduled commands are ready to run. (as expected when jobs aren’t due).
  • I now understand the crontab line is required to trigger Laravel’s scheduler every minute. Laravel then handles internal scheduling (i.e., “is this job due?”).

Final Question

  • Is status_id: 1 (Draft) the correct starting point for recurring invoices via API?
  • Is there any way to create recurring invoices directly in Approved state?
  • Is the bulk/start API method enough to trigger everything (including invoice creation and email), assuming cron is configured correctly?

Cron Config

* * * * * docker exec -u www-data debian_app_1 php artisan schedule:run >> /dev/null 2>&1

Screenshots

Logs

No errors in storage/logs/laravel.log.

Hi,

Please check that auto-email is enabled in Settings > Workflow Settings

1 Like

Good news!
I went to sleep, and by the time I woke up, the email had arrived in my inbox :raising_hands:

The invoice status went from Pending to Active, so things are moving!


I’ve already configured Payment Terms under Settings → Payment Settings to NET 5 (5 days after the invoice is sent).

Now I’m wondering:

  • What exactly happens on Day 6, when the invoice is officially due?
    Does the status just change to Due, or is there another trigger?
  • What if someone pays it after it’s due — is it processed normally, or flagged?

Also:

  • How many reminder emails does the system send for a recurring invoice before it’s paid?
  • Is it possible to configure it to send a reminder every day until payment is made?

And lastly, given the following function, what time it should send the emails for recurring invoices? even though I received it, I do not understand why at that time.

/**
 * Create a recurring invoice for a client (initially as draft).
 * Later you can start it using `startRecurringInvoicesNow()`
 */
async function createRecurringInvoice(clientId) {
  try {
    if (!clientId) {
      throw new Error("❌ client_id is missing! Please provide a valid client ID.");
    }

    const today = new Date().toISOString().split("T")[0];

    const invoiceData = {
      client_id: clientId,
      frequency_id: "4", // Monthly
      remaining_cycles: -1,
      auto_bill: "always",
      currency_id: "1", // USD
      start_date: today,
      next_send_date: today,
      line_items: [
        {
          product_key: "Example Subscription",
          notes: "Monthly subscription for Example access",
          cost: DEFAULT_INVOICE_AMOUNT,
          quantity: 1,
          tax_id: "1",
          type_id: "1", // Product
          product_cost: 10.0,
        },
      ],
    };

    const createResponse = await axios.post(
      `${process.env.NINJA_API_URL}/api/v1/recurring_invoices`,
      invoiceData,
      {
        headers: {
          "X-API-TOKEN": process.env.NINJA_API_TOKEN,
          "X-Requested-With": "XMLHttpRequest",
          "Content-Type": "application/json",
        },
      }
    );

    const invoiceId = createResponse.data.data.id;
    createdInvoiceIds.push(invoiceId); // Track for later bulk start

    console.log(`📝 Created draft recurring invoice ${invoiceId} for client ${clientId}`);
    return invoiceId;
  } catch (error) {
    if (error.response) {
      console.error(`❌ API Error: ${error.response.status} - ${error.response.statusText}`);
      console.error(`❌ Response Data:`, JSON.stringify(error.response.data, null, 2));
    } else {
      console.error(`❌ Error creating recurring invoice:`, error.message);
    }
  }
}

Thanks in advance if anyone has insights! :blush:

The due date can be used to trigger auto billing, reminders, late fees, etc.

You can configure endless reminders which keep sending.

1 Like