Source: https://billingmart.com/developers/api/recurring # Recurring and automation Schedules that raise invoices on their own, the reminder cadence, and what the scheduled jobs will do next. [All sections](https://billingmart.com/developers/api) [Authentication](https://billingmart.com/developers/authentication) **On this page** - GET List - GET Get - PATCH Update - POST Pause - GET Upcoming - GET List - GET Get - OBJ Recurring - OBJ AutomationUpcoming - OBJ SchedulerRun - OBJ DunningCadenceView On this page - GET List - GET Get - PATCH Update - POST Pause - GET Upcoming - GET List - GET Get - OBJ Recurring - OBJ AutomationUpcoming - OBJ SchedulerRun - OBJ DunningCadenceView recurring ## List GET `/v1/recurring` List recurring schedules. Scope `recurring:read` Effect Read only MCP tool `list_recurring` Retries Safe to repeat ### Parameters List parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `limit`optional | query | `integer` | min 1 · max 50 | | `cursor`optional | query | `string` | up to 256 chars | | `clientId`optional | query | `id` | — | | `active`optional | query | `boolean` | — | ### Returns `data`: [`Recurring[]`](https://billingmart.com/developers/api/recurring#object-Recurring) . Pass `meta.cursor` back as `cursor` for the next page. Example request Sample ``` curl -X GET "https://api.billingmart.com/v1/recurring" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` recurring ## Get GET `/v1/recurring/{recurringId}` One recurring schedule. Scope `recurring:read` Effect Read only MCP tool `get_recurring` Retries Safe to repeat ### Parameters Get parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `recurringId`required | path | `id` | — | ### Returns `data`: [`Recurring`](https://billingmart.com/developers/api/recurring#object-Recurring) Example request Sample ``` curl -X GET "https://api.billingmart.com/v1/recurring/{recurringId}" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` recurring ## Update PATCH `/v1/recurring/{recurringId}` Update a recurring schedule’s client, lines, title, terms or end, or pause or resume it. A new client removes any autopay. Autopay is NOT settable here — it needs recorded consent. A resume that would issue invoices for dates before today answers 409 MISSED_OCCURRENCES with the count, until `missed` is `skip` (they are never issued; an installment plan keeps every installment) or `issue` (they go out on the next run). Scope `recurring:write` Effect Writes a draft or record MCP tool `update_recurring` Retries No Idempotency-Key on this route ### Parameters Update parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `recurringId`required | path | `id` | — | ### Request body JSON. Money is an integer count of minor units beside its currency; quantities are thousandths. Update request body | Field | Type | Notes | | --- | --- | --- | | `expectedUpdatedAt`optional | `string` | up to 40 chars | | `clientId`optional | `id` | — | | `title`optional | `string \| null` | — | | `lineItems[]`optional | `object[]` | up to 200 items | | `lineItems[].id`optional | `id` | — | | `lineItems[].description`optional | `string` | What the line is for. Optional with a presetKey (the item’s name is used). — up to 2000 chars | | `lineItems[].quantityMilli`optional | `integer` | Quantity in thousandths: 1000 is one unit, 1500 is 1.5 hours. Defaults to 1000. | | `lineItems[].rateBasis`optional | `enum` | How the price applies: fixed for one flat charge, hourly, daily, perUnit, percentage… Optional with a presetKey. — One of `fixed`, `hourly`, `daily`, `weekly`, `monthly`, `perUnit`, `perMile`, `perKilometre`, `percentage` | | `lineItems[].unitPrice`optional | `object` | Price per unit in integer minor units of the document currency ($5,000.00 is 500000). | | `lineItems[].unitPrice.minorUnits`required | `integer` | — | | `lineItems[].unitPrice.currency`required | `Currency` | — | | `lineItems[].rateBasisPoints`optional | `integer` | — | | `lineItems[].taxTreatment`optional | `string` | — | | `lineItems[].taxRateBasisPoints`optional | `integer` | — | | `lineItems[].discountBasisPoints`optional | `integer` | — | | `lineItems[].presetKey`optional | `string` | The key of one of the business’s products and services (get_business_context lists them); the server fills what the line leaves out. — up to 64 chars | | `netDays`optional | `integer \| null` | — | | `endDate`optional | `string \| null` | — | | `occurrenceLimit`optional | `integer \| null` | — | | `active`optional | `boolean` | — | | `missed`optional | `enum` | One of `skip`, `issue` | ### Returns `data`: [`Recurring`](https://billingmart.com/developers/api/recurring#object-Recurring) Example request Sample ``` curl -X PATCH "https://api.billingmart.com/v1/recurring/{recurringId}" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` recurring ## Pause POST `/v1/recurring/{recurringId}/pause` Pause a recurring schedule: no invoice is issued or charged until a person resumes it. Resuming is an action a person approves. Scope `recurring:write` Effect Writes a draft or record MCP tool `pause_recurring` Retries Send an Idempotency-Key; a repeat replays the stored answer for 24 hours ### Parameters Pause parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `recurringId`required | path | `id` | — | ### Returns `data`: `object` Errors this route can answer besides the shared ones: `NOT_FOUND` . Example request Sample ``` curl -X POST "https://api.billingmart.com/v1/recurring/{recurringId}/pause" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` automation ## Upcoming GET `/v1/automation/upcoming` What each scheduled job will do next for this workspace, computed from the SAME selection functions the schedulers run (never a second copy): the next tick in UTC and in the workspace’s own time, the records it would act on, and why. Scope `business:read` Effect Read only MCP tool `automation_upcoming` Retries Safe to repeat ### Parameters Upcoming parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `job`optional | query | `string` | up to 40 chars | | `limit`optional | query | `integer` | min 1 · max 50 | ### Returns `data`: [`AutomationUpcoming`](https://billingmart.com/developers/api/recurring#object-AutomationUpcoming) Example request Sample ``` curl -X GET "https://api.billingmart.com/v1/automation/upcoming" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` scheduler runs ## List GET `/v1/automation/runs` What the scheduled jobs actually did, newest first. A row exists only for a tick that acted on or failed for this workspace. Scope `business:read` Effect Read only MCP tool `list_scheduler_runs` Retries Safe to repeat ### Parameters List parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `limit`optional | query | `integer` | min 1 · max 50 | | `cursor`optional | query | `string` | up to 256 chars | | `job`optional | query | `string` | up to 40 chars | ### Returns `data`: [`SchedulerRun[]`](https://billingmart.com/developers/api/recurring#object-SchedulerRun) . Pass `meta.cursor` back as `cursor` for the next page. Example request Sample ``` curl -X GET "https://api.billingmart.com/v1/automation/runs" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` dunning ## Get GET `/v1/dunning/cadence` Each business’s reminder cadence — the day offsets relative to the due date and when chasing stops — and whether it is the business’s own or the platform default. Scope `business:read` Effect Read only MCP tool `get_dunning_cadence` Retries Safe to repeat ### Parameters Get parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `businessId`optional | query | `id` | — | ### Returns `data`: [`DunningCadenceView`](https://billingmart.com/developers/api/recurring#object-DunningCadenceView) Example request Sample ``` curl -X GET "https://api.billingmart.com/v1/dunning/cadence" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` Object ## Recurring A schedule that issues invoices. Each issue is a real send: it mints a number and meters against the tenant's plan in the tenant's own timezone. Autopay charges the vaulted method server-side; the amount is computed at charge, never carried on the schedule. Recurring fields | Field | Type | Notes | | --- | --- | --- | | `id`required | `Id` | — | | `tenant`required | `TenantRef` | — | | `merchantAccountId`optional | `Id` | The merchant account the invoices of this schedule are routed through. Absent means the business default, then the workspace default account. | | `clientId`required | `Id` | — | | `title`optional | `string` | Untrusted text — | | `frequency`required | `RecurringFrequency` | — | | `anchorDay`optional | `integer` | Day of the period to issue on. A 31 in a 30-day month clamps to the last day; that rule lives and is stated here so two implementations cannot disagree. | | `startDate`optional | `DateOnly` | — | | `endDate`optional | `DateOnly` | — | | `occurrenceLimit`optional | `integer` | 600, what the server and REST accept. Counts ISSUED occurrences: skipped ones (below) do not use it up. | | `skippedOccurrences`optional | `integer` | SERVER-WRITTEN. Occurrences passed over on purpose: when a resume, a past start date or a timing edit would have issued invoices for dates before today, the caller chose `missed: skip`. | | `occurrencesIssued`optional | `integer` | SERVER-WRITTEN. | | `nextRunAt`optional | `IsoTimestamp` | SERVER-WRITTEN. Resolved from anchorDay in the tenant's timezone. | | `lastRunAt`optional | `IsoTimestamp` | — | | `capRefused`optional | `object` | SERVER-WRITTEN. The send cap refused this schedule's occurrence `occurrence` at `at`, in the usage month `period`, on plan `plan`. | | `blockedAt`optional | `IsoTimestamp` | SERVER-WRITTEN, like nextRunAt. When the runner parked this schedule because it could not issue (a required invoice detail is missing). Absent on a schedule that is not parked. | | `blockedCode`optional | `string` | SERVER-WRITTEN. The error code the runner stopped on. A code, never tenant text. | | `blockedDraftUpdatedAt`optional | `IsoTimestamp` | SERVER-WRITTEN. The updatedAt of the draft invoice the blocked run left behind, so a retry can tell whether it was changed since. | | `blockedScheduleUpdatedAt`optional | `IsoTimestamp` | SERVER-WRITTEN. The schedule's own updatedAt when it was parked: an edit after this moment means the block may be resolved and the runner tries again. | | `currency`required | `Currency` | — | | `language`required | `Language` | — | | `feeOverride`optional | `InvoiceFeeOverride` | Copied onto every invoice this schedule issues. Absent means the workspace default. | | `lineItems`required | `LineItem[]` | — | | `customFields`optional | `object` | Untrusted text key, copied by the runner onto every invoice the schedule issues. A required field with no value here is why a schedule cannot issue. | | `installmentTotal`optional | `Money` | An installment plan: the whole amount split over occurrenceLimit occurrences by the money module's exact split — the last installment absorbs the remainder. Absent on an ordinary schedule. Additive. | | `netDays`optional | `integer` | — | | `autopay`optional | `AutopayConfig` | — | | `active`required | `boolean` | — | | `createdAt`required | `IsoTimestamp` | — | | `updatedAt`optional | `IsoTimestamp` | — | | `createdBy`optional | `Id` | — | | `lastWriteActor`optional | `WriteActor` | Who last wrote this record, and when — stamped by the server core. An EDIT_CONFLICT names it, so the person refused knows who saved the newer version. | Object ## AutomationUpcoming AutomationUpcoming is computed by the server for this request, not a stored document, so no contract describes it. Its fields are the TypeScript type of the same name that the handler returns. Object ## SchedulerRun What one scheduled job did for ONE tenant on one tick. SchedulerRun fields | Field | Type | Notes | | --- | --- | --- | | `id`required | `Id` | — | | `tenant`required | `TenantRef` | — | | `job`required | `enum` | Which scheduled function. The same names `automation_upcoming` answers with, so what-will-run and what-ran line up. | | `startedAt`required | `IsoTimestamp` | The tick's instant, and the second half of the document id. | | `finishedAt`required | `IsoTimestamp` | — | | `acted`required | `integer` | How many records the job acted on for this tenant. | | `failed`required | `integer` | How many it tried and could not. A row exists whenever this or `acted` is above zero. | | `recordIds`optional | `Id[]` | The ids acted on, capped at 50 by the writer. `truncated` says when there were more. | | `truncated`optional | `boolean` | True when the job acted on more records than `recordIds` carries. | | `errorCodes`optional | `string[]` | Catalogue codes for what failed, de-duplicated and capped at 20 by the writer. Never a provider body and never tenant text: a code only, so reading the log can never be a way to read a customer's data. | | `note`optional | `string` | A short, SERVER-WRITTEN fact the counts cannot carry — `skipped: no gateway credentials`, `purged`. Never tenant- or agent-supplied text. | | `createdAt`required | `IsoTimestamp` | — | | `updatedAt`optional | `IsoTimestamp` | — | Object ## DunningCadenceView DunningCadenceView is computed by the server for this request, not a stored document, so no contract describes it. Its fields are the TypeScript type of the same name that the handler returns. Next section ## Time, expenses, mileage and documents Work logged during the month, billed onto an invoice exactly once, and paper read from a photo or PDF into a draft a person files. A CSV of clients, items or open invoices can be dry-run here; a person runs the import in the console. [Continue to Time, expenses, mileage and documents →](https://billingmart.com/developers/api/capture) ## Try it against your sandbox Create a bmk_test_ key in the console. It can only reach your sandbox workspace. [Start free](https://app.billingmart.com/sign-up) [All sections](https://billingmart.com/developers/api) --- This is the Markdown copy of https://billingmart.com/developers/api/recurring. Live plans, prices and limits are published as JSON at https://billingmart.com/plans.json and https://billingmart.com/agent-info.json.