Skip to main content

Patterns

Recurring invoices and subscriptions

Monthly/yearly cadence, draft vs auto-send, occurrence idempotency

A recurring invoice profile (`recurring_invoices`) describes a buyer, line items and a cadence (`recurrence`: day/week/month/year + `day_of_month`/`day_of_week`). At each due date the scheduler emits an occurrence. Two modes: `emission_mode='draft'` stages each occurrence as a draft (manual review before sending); `auto_send` emits AND sends automatically. Termination is controlled via `end_mode` (`never`, `on_date` + `end_date`, `after_occurrences` + `max_occurrences`). The critical fiscal pattern: each occurrence is a full ISCA invoice (hash chain, sequential numbering). Emission idempotency prevents a scheduler re-run from doubling an occurrence on the same day. `runNow()` forces an immediate emission (test/catch-up); `pause()`/`activate()` suspend the cadence without losing history.

Key facts

  • Cadence: `recurrence.interval_unit` (day/week/month/year) + day_of_*
  • `emission_mode`: `draft` (review) or `auto_send`
  • `end_mode`: never / on_date / after_occurrences
  • Each occurrence = full ISCA invoice (hash + sequence)
  • Idempotency: a scheduler re-run does not double the occurrence
  • Endpoints: `/api/v1/recurring-invoices` + `/{id}/run-now`, `/pause`, `/activate`

Code example

import { ScellApiClient } from '@scell/sdk'; // v3.5.0

const client = new ScellApiClient({ apiKey: process.env.SCELL_API_KEY! });

// 1. Profil d'abonnement mensuel auto-envoyé, le 1er de chaque mois
const profile = await client.recurringInvoices.create({
  title: 'Abonnement mensuel — Acme',
  buyer_id: 'buyer-uuid', // ou champs buyer_* inline
  currency: 'EUR',
  output_format: 'facturx',
  emission_mode: 'auto_send',
  start_date: '2026-07-01',
  recurrence: { interval_unit: 'month', interval_count: 1, day_of_month: 1 },
  end_mode: 'never',
  notify_before_days: 3,
  lines: [{ description: 'Abonnement SaaS', quantity: 1, unit_price: 99, vat_rate: 20 }],
});

// 2. Lister les occurrences déjà émises
const { data: occurrences } = await client.recurringInvoices.occurrences(profile.id, { per_page: 12 });
console.log(`${occurrences.length} occurrences émises`);

// 3. Émission immédiate (rattrapage / test)
const run = await client.recurringInvoices.runNow(profile.id);
console.log('Occurrence forcée :', run);

// 4. Suspendre puis réactiver sans perdre l'historique
await client.recurringInvoices.pause(profile.id);
await client.recurringInvoices.activate(profile.id);

See also

Your cookie preferences

We use cookies to improve your experience. Essential cookies are always active. Cookie policy.