CompanyRecurringInvoices

Update a recurring invoice

Updates only the fields present in the body, leaving every other field of the recurring invoice template as it is.

  • Omitted vs null: an omitted field keeps its current value; a field sent as null is cleared, and only where the request schema documents the field as nullable.
  • lines: replaced as a whole, not patched line by line. The recipient survives the change, and an empty array is rejected.
  • payment_method: replaced as a whole together with payment_iban, payment_swift and payment_term_days — send them in the same request or they are dropped.
  • Schedule: frequency, day_of_month and start_date stay put unless you send them; sending a new value for any of the three moves the next generation — resending the ones already in effect changes nothing. Changing frequency recalculates it on the new grid and discards a pending skip. start_date is only editable while the template has not generated any invoice yet.
  • custom_fields: replaced as a whole; omit it to keep the current ones, send null or [] to remove them all. Invoices already generated keep theirs.
patch/v1/companies/{company_id}/recurring-invoices/{recurring_invoice_id}

Request

  • Base URL: https://app.beel.es/api
  • URL: https://app.beel.es/api/v1/companies/{company_id}/recurring-invoices/{recurring_invoice_id}
  • Auth: HTTP bearer

Headers

Idempotency-Keystring

Idempotency key to prevent duplicates in sensitive operations.

  • Any unique client-generated string (e.g. an order id). A UUID also works but is not required
  • Allowed characters: letters, digits, _ and - (max 255 chars)
  • Retrying with the same key replays the first response when it was a success (2xx) or a server error (5xx): same status and body, plus the header Idempotency-Replay: true. After a 5xx, check whether the operation took effect before retrying with a new key
  • A 4xx is not stored: the key is released, so the corrected request can reuse it
  • Stored responses expire 24 hours after processing

The key is scoped per user and environment, and bound to the request body, so retrying after a network timeout replays the stored response instead of repeating the operation.

StatusCodeWhen
400INVALID_IDEMPOTENCY_KEYThe key breaks the format rules above.
409IDEMPOTENCY_KEY_PROCESSINGThe first request is still in flight. Wait for the Retry-After seconds (2) and retry with the same key.
409IDEMPOTENCY_KEY_MISMATCHThe key was already used with a different body. Use a new key.

Request body

namestring

Template name. Cannot be cleared.

frequency'MONTHLY' | 'QUARTERLY' | 'YEARLY'

Generation cadence: MONTHLY every month, QUARTERLY every 3 months, YEARLY every 12 months. Omitted, the current one is kept — it is never reset to MONTHLY.

Changing it reschedules the template. The next generation is recalculated on the new grid — the day_of_month dates anchored at start_date, one every cadence — so it can land months later than the one you saw before the call. If the template has already issued invoices, the landing respects that: it is never a period that was already billed, and never before today either — the first point of the new grid strictly after the last invoiced one. It also discards a pending skip: the skipped period only existed as a point of the old grid, and that grid is gone. Send the same cadence and nothing is rescheduled.

day_of_monthinteger

Day of the month the invoices are issued. Moves the next generation immediately, on an ACTIVE template and on a PAUSED one alike: the response to this very call already carries the new next_generation. On a paused template the new day rules from the edit, not from the day you resume — resuming keeps the date this call left, so editing the day of a paused schedule is never ignored for a round.

A month that does not have that day falls back to its last day: a template on 31 issues on 28 February (29 in a leap year) and on 30 April. So 31 is how you ask for the last day of the month it generates in — there is no separate flag for it, and the accepted range stays 1–31.

The adjustment does not stick and the schedule does not drift: every generation is recalculated from the day_of_month you sent, never from the date it was adjusted to (31 Jan → 28 Feb → 31 Mar).

start_datestring date

First issue date. Only changeable while the template has not generated any invoice yet: once it has issued, a different date is rejected with a 422 RECURRING_START_DATE_NOT_EDITABLE. Sending the value it already has is a no-op and succeeds, so a read-modify-write cycle never has to strip the field out of the body.

A past date is accepted and stored as sent, but it never anchors generation in the past: next_generation becomes the first date of the template's own calendar on or after today (today included) — the grid of day_of_month dates anchored at start_date, one every frequency — which on a quarterly or yearly template can be months from now. Creating and updating apply the same rule. If today's period was already invoiced in the system you are migrating from, send a start_date of tomorrow (or of the next period) so it is not invoiced twice.

draft_in_advanceboolean

Whether the template prepares a draft for review before emitting. The window is fixed at 5 days and both options emit on the scheduled day. Generating manually a period whose scheduled date is today or earlier emits it on the spot. Omitted, the current value is kept.

preview_daysinteger

Deprecated. Superseded by draft_in_advance. Any value greater than 0 means the same as draft_in_advance: true, and 0 the same as false. When both are sent, draft_in_advance wins. It will be removed in a future version.

end_datestring date nullable

Date the recurrence stops. Send null to make it open-ended.

max_invoicesinteger nullable

Total number of invoices this template will generate before ending on its own, between 2 and 600. Send null to stop capping the recurrence by number.

Editing it MOVES THE GOAL, it does not add turns: above the invoices already generated the template stays active; exactly equal ends it in this very call (with completion.reason = MAX_INVOICES_REACHED); below it the call is rejected — issued invoices are fiscal facts, and someone typing a lower number is asking to stop now, which is what ending the recurrence is for.

A recurrence ends in ONE way, and the conflict is judged on the RESULTING state, not on the body: sending max_invoices to a template that already has an end_date is rejected with RECURRING_END_MODE_CONFLICT even though the body only mentions one of them. Switching mode is a single call that says both things — the new field with a value and the old one as null. Nothing is cleared silently.

series_idstring uuid

Series the generated invoices are numbered in. Cannot be cleared.

invoice_type'STANDARD' | 'SIMPLIFIED'

Type of the invoices a recurring template generates: STANDARD for an identified recipient, SIMPLIFIED for a recipient that is not identified.

customer_idstring uuid nullable

Recipient of the generated invoices. Send null to leave the template without a recipient; omit it to keep the current one.

payment_method'NONE' | 'BANK_TRANSFER' | 'CARD' | 'CASH' | 'CHECK' | 'DIRECT_DEBIT' | 'BIZUM' | 'OTHER'

Payment method shown on invoices and recurring invoices.

  • NONE: no payment information is shown.
  • BANK_TRANSFER: bank transfer to the IBAN of the payment details; the only method that requires an IBAN.
  • CARD: card payment.
  • CASH: cash payment.
  • CHECK: payment by cheque.
  • DIRECT_DEBIT: direct debit from the customer's bank account.
  • BIZUM: payment through Bizum.
  • OTHER: any other method.
payment_ibanstring nullable
payment_swiftstring nullable
payment_term_daysinteger nullable
notesstring nullable

Notes printed on the generated invoices. Send null to clear them.

send_automaticallyboolean

Example request

{
  "max_invoices": 6,
  "lines": [
    {
      "unit_price": 50,
      "total_excluding_tax": 1,
      "total_including_tax": 100,
      "exemption_reason": "EXENTA_ART_20"
    }
  ],
  "payment_method": "BANK_TRANSFER",
  "custom_fields": [
    {
      "key": "store",
      "value": "Gran Vía 12"
    }
  ],
  "email_configuration": {
    "recipients": [
      "user@example.com"
    ],
    "cc": [
      "user@example.com"
    ]
  }
}

Response

Recurring invoice updated successfully

successboolean required

Example response

{
  "success": true,
  "data": {
    "draft_in_advance": true,
    "status": "ACTIVE",
    "pause": {
      "reason": "GENERATION_FAILURE",
      "blocker": "NIF_NOT_REGISTERED"
    },
    "lines": [
      {
        "pricing_mode": "UNIT_PRICE",
        "total_excluding_tax": 1,
        "total_including_tax": 100,
        "exemption_reason": "EXENTA_ART_20"
      }
    ],
    "amount": 106,
    "custom_fields": [
      {
        "key": "project",
        "label": "Project",
        "value": "Corporate website"
      }
    ],
    "max_invoices": 6,
    "last_generated_scheduled_date": "2026-05-01"
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.